From patchwork Fri May 15 21:48:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Almeida X-Patchwork-Id: 209794 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B7711C433DF for ; Fri, 15 May 2020 21:48:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 94A442070A for ; Fri, 15 May 2020 21:48:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="frXfUUqE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727940AbgEOVsn (ORCPT ); Fri, 15 May 2020 17:48:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47130 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1726266AbgEOVsn (ORCPT ); Fri, 15 May 2020 17:48:43 -0400 Received: from mail-qk1-x744.google.com (mail-qk1-x744.google.com [IPv6:2607:f8b0:4864:20::744]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 31493C061A0C; Fri, 15 May 2020 14:48:43 -0700 (PDT) Received: by mail-qk1-x744.google.com with SMTP id 190so4250603qki.1; Fri, 15 May 2020 14:48:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=/bMUwdeTMh2W9i+EOVv7dhyz664fp1ziAg67LLNKBOI=; b=frXfUUqE34wjGzrPt/UpX6qWWgh222sCoLJ9clKhWElbz17fHBYpYXjAI4efHyhqNh nhK45dynSmhoiiVVdg4BFVtA9cI9qnJCP14a2zgekPK6WytP1ZA3+DTlAcHVq0R/BJdk cNOqXpLcH1NG1SasrtTmnG7szh0NQcyz+lYAV2vVaXlIDHqyCBLrAURYfrajxa9x/m2H JQEDINlKlG2D6fHnVsl1YydjCIez+BLbYIujtMXwJh6SDsj+BUdMkc3agTpBNEy2DyHo fR+kT75oT2Q7WTPCg7XpoNzZzMX8Ivusd+o7fM4WDQIQuDEr58DEVUNDMUjby39VMy1M CRwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/bMUwdeTMh2W9i+EOVv7dhyz664fp1ziAg67LLNKBOI=; b=EXO7rKGdoD7koaW7916E3rMxsa81RNrku/j6Jtu1hK+BIgAYLluqPfwlCQrf4eodW0 W27tCI36nj2QHduSGHP3uLxygKhd7jg7Zu7hz0n2GVrfEEcm+zsZB6I9K7tZrgHxSP2o OULiDfV2eoLXH3VLbh6t41g3fm6pwnPVtx9YWWKho6Mf2mpNRRTHCaozy1Infh6CKQHX pgDS75zUlulOFo2un4K4KNnyBJ6Xg6Tu68tLwXVsw1EZ+kD7veSds0qmAUwTjYMyMgqf hSBkyhjtK3ZlZVyjdij8qav9OfAu15whN7fMKUP/0Vdi8gV0kThwRGNit/j1wsg0XU49 2B/w== X-Gm-Message-State: AOAM533WBF8B2gGC3jo4p9ZL8u6uQX/7h85xrLqSaoggMBZwo6M/RDk1 RChu17G8fr68Zagj1QVM4As= X-Google-Smtp-Source: ABdhPJxBYveqgHbL6PYIQcIWYaOZD3nhsEZ/WdrV3LcpuHRSJ+RZzaYXaSvg11WdbuNMWUdbck0ZmQ== X-Received: by 2002:a37:7403:: with SMTP id p3mr5656512qkc.339.1589579322358; Fri, 15 May 2020 14:48:42 -0700 (PDT) Received: from localhost.localdomain ([2804:18:700e:2692:8980:dd13:6be6:4050]) by smtp.gmail.com with ESMTPSA id m21sm2495019qkh.50.2020.05.15.14.48.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2020 14:48:41 -0700 (PDT) From: "Daniel W. S. Almeida" X-Google-Original-From: Daniel W. S. Almeida To: mchehab+huawei@kernel.org, sean@mess.org, kstewart@linuxfoundation.org, allison@lohutok.net, tglx@linutronix.de Cc: "Daniel W. S. Almeida" , linux-media@vger.kernel.org, skhan@linuxfoundation.org, linux-kernel-mentees@lists.linuxfoundation.org, linux-kernel@vger.kernel.org Subject: [RFC, WIP, v5 01/10] media: vidtv: add Kconfig entry Date: Fri, 15 May 2020 18:48:06 -0300 Message-Id: <20200515214815.15514-2-dwlsalmeida@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200515214815.15514-1-dwlsalmeida@gmail.com> References: <20200515214815.15514-1-dwlsalmeida@gmail.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: "Daniel W. S. Almeida" Add the necessary Kconfig entries and a dummy Makefile to compile the new virtual DVB test driver (vidtv). Signed-off-by: Daniel W. S. Almeida --- drivers/media/test-drivers/Kconfig | 10 ++++++++++ drivers/media/test-drivers/Makefile | 1 + drivers/media/test-drivers/vidtv/Kconfig | 11 +++++++++++ drivers/media/test-drivers/vidtv/Makefile | 2 ++ 4 files changed, 24 insertions(+) create mode 100644 drivers/media/test-drivers/vidtv/Kconfig create mode 100644 drivers/media/test-drivers/vidtv/Makefile diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig index 188381c855939..7d273a8a7acc2 100644 --- a/drivers/media/test-drivers/Kconfig +++ b/drivers/media/test-drivers/Kconfig @@ -4,6 +4,10 @@ menuconfig V4L_TEST_DRIVERS bool "V4L test drivers" depends on VIDEO_DEV +menuconfig DVB_TEST_DRIVERS + bool "DVB test drivers" + depends on DVB_CORE && MEDIA_SUPPORT && I2C + if V4L_TEST_DRIVERS source "drivers/media/test-drivers/vimc/Kconfig" @@ -24,3 +28,9 @@ config VIDEO_VIM2M source "drivers/media/test-drivers/vicodec/Kconfig" endif #V4L_TEST_DRIVERS + +if DVB_TEST_DRIVERS + +source "drivers/media/test-drivers/vidtv/Kconfig" + +endif #DVB_TEST_DRIVERS diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile index 74410d3a9f2d2..9f0e4ebb2efe7 100644 --- a/drivers/media/test-drivers/Makefile +++ b/drivers/media/test-drivers/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_VIDEO_VIMC) += vimc/ obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ +obj-$(CONFIG_DVB_VIDTV) += vidtv/ diff --git a/drivers/media/test-drivers/vidtv/Kconfig b/drivers/media/test-drivers/vidtv/Kconfig new file mode 100644 index 0000000000000..22c4fd39461f1 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DVB_VIDTV + tristate "Virtual DVB Driver (vidtv)" + depends on DVB_CORE && MEDIA_SUPPORT && I2C + help + The virtual DVB test driver serves as a reference DVB driver and helps + validate the existing APIs in the media subsystem. It can also aid developers + working on userspace applications. + + + When in doubt, say N. diff --git a/drivers/media/test-drivers/vidtv/Makefile b/drivers/media/test-drivers/vidtv/Makefile new file mode 100644 index 0000000000000..d1558d84eeaed --- /dev/null +++ b/drivers/media/test-drivers/vidtv/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 + From patchwork Fri May 15 21:48:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Almeida X-Patchwork-Id: 209793 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7B02DC433E2 for ; Fri, 15 May 2020 21:48:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4A9BF2070A for ; Fri, 15 May 2020 21:48:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fSZGatxs" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727983AbgEOVsy (ORCPT ); Fri, 15 May 2020 17:48:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47162 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1726266AbgEOVsx (ORCPT ); Fri, 15 May 2020 17:48:53 -0400 Received: from mail-qt1-x841.google.com (mail-qt1-x841.google.com [IPv6:2607:f8b0:4864:20::841]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 52B7AC061A0C; Fri, 15 May 2020 14:48:53 -0700 (PDT) Received: by mail-qt1-x841.google.com with SMTP id 4so3327686qtb.4; Fri, 15 May 2020 14:48:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=jJo1hcKeIy0jwOOtOttyjeXDb5p70U+Kv4mBIT4Zv9o=; b=fSZGatxs1F5l70Jm1Dei/M8YJfxmlekHJZvwQQ3Psc5flreuvI+VNRfw08HenFS1UO qOsb//c0dB2Goog4EzmOvyBC8bXNPWdwrLz9ZKY6BkkBOtAy3xLB8rfaDQKjo7a5BaLv rGx5UUygTWHg2YHxZ6I7Ly0hsjITBRCdFtr1BTBLSOP3H7ONwcIt6gh4Dcq7q7TJvMyp lXXzWNAVJtdAj8QuZrleUnFD3aSqhot+9c+EURC6/YWxQUNwyrrDxLb4H2zRhfQlC83K 922Ltx0Qnd8winDezjlHHh2C/dj7p00U9I5Y4kAIzhLXbLi8eCXJ8tFggrQykKxTOi4H R1Lg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jJo1hcKeIy0jwOOtOttyjeXDb5p70U+Kv4mBIT4Zv9o=; b=sBSVElEVA2TcrFiqw/zdJo5x+Ax+NsyzWXILyHhdXMGj2FW48NsMwTPdEl5PvpmccG /dLuOq9LPtBvkbjTlWcROVcjWjFRfB3Ud5idAtqS+h+Ave1Cst+p0HdDwkI6TypCrgvz o3lEdBKd8xL94uNaNI6MjrLIgcbREaZUsKbAnRlv1N7dxR83khEFojEQxmmkFXcAPSE5 h/1P9gFMe5aBoMRwR6alNLJ/O7DQ3m/7JpawPk93zbLgNZ10mU7/B5fOUtw8ORPT7hIL FB5DhVCfM/7bLvrKn9NW1Vke9ehKUpldK3VPzkANqbv5/4DHhLA3PNm/pbgBpHzd4gsn 39Qw== X-Gm-Message-State: AOAM533Mj3aSl/8lLyPzPn8UFKRXU53YessPkP8V7bt16Jc9kjRpdSsz iFEjAJzbhFQCG9GdTlOGw1w= X-Google-Smtp-Source: ABdhPJyKxFG9LOZEqdimaf+DJcz4d7KYrxHmag1d1sOmZ4K9JEdgQ0wWo2sRWqJ2jTkLaI/HlYuw7Q== X-Received: by 2002:ac8:27ed:: with SMTP id x42mr5786351qtx.231.1589579332380; Fri, 15 May 2020 14:48:52 -0700 (PDT) Received: from localhost.localdomain ([2804:18:700e:2692:8980:dd13:6be6:4050]) by smtp.gmail.com with ESMTPSA id m21sm2495019qkh.50.2020.05.15.14.48.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2020 14:48:51 -0700 (PDT) From: "Daniel W. S. Almeida" X-Google-Original-From: Daniel W. S. Almeida To: mchehab+huawei@kernel.org, sean@mess.org, kstewart@linuxfoundation.org, allison@lohutok.net, tglx@linutronix.de Cc: "Daniel W. S. Almeida" , linux-media@vger.kernel.org, skhan@linuxfoundation.org, linux-kernel-mentees@lists.linuxfoundation.org, linux-kernel@vger.kernel.org Subject: [RFC, WIP, v5 03/10] media: vidtv: implement a demodulator driver Date: Fri, 15 May 2020 18:48:08 -0300 Message-Id: <20200515214815.15514-4-dwlsalmeida@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200515214815.15514-1-dwlsalmeida@gmail.com> References: <20200515214815.15514-1-dwlsalmeida@gmail.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: "Daniel W. S. Almeida" Implement a I2C demodulator driver, simulating support for DVB-T, DVB-C and DVB-S. This demodulator will periodically check the signal quality against a table and drop the TS lock if it drops below a threshold value, regaining it in the event that the signal improves. Signed-off-by: Daniel W. S. Almeida --- drivers/media/test-drivers/vidtv/Makefile | 2 +- .../media/test-drivers/vidtv/vidtv_demod.c | 448 ++++++++++++++++++ .../media/test-drivers/vidtv/vidtv_demod.h | 42 ++ 3 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 drivers/media/test-drivers/vidtv/vidtv_demod.c create mode 100644 drivers/media/test-drivers/vidtv/vidtv_demod.h diff --git a/drivers/media/test-drivers/vidtv/Makefile b/drivers/media/test-drivers/vidtv/Makefile index e625810a82603..36ba00ddc0d1e 100644 --- a/drivers/media/test-drivers/vidtv/Makefile +++ b/drivers/media/test-drivers/vidtv/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_DVB_VIDTV) += vidtv_tuner.o +obj-$(CONFIG_DVB_VIDTV) += vidtv_tuner.o vidtv_demod.o diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c new file mode 100644 index 0000000000000..2d5270e71a909 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Written by Daniel W. S. Almeida + * Based on the example driver written by Emard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vidtv_demod.h" + +MODULE_DESCRIPTION("Virtual DVB Demodulator Driver"); +MODULE_AUTHOR("Daniel W. S. Almeida"); +MODULE_LICENSE("GPL"); + +#define POLL_THRD_TIME 2000 /* msecs */ + +static struct vidtv_demod_cnr_to_qual_s vidtv_demod_c_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QAM_256, FEC_NONE, 34000, 38000}, + { QAM_64, FEC_NONE, 30000, 34000}, +}; + +static struct vidtv_demod_cnr_to_qual_s vidtv_demod_s_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QPSK, FEC_1_2, 7000, 10000}, + { QPSK, FEC_2_3, 9000, 12000}, + { QPSK, FEC_3_4, 10000, 13000}, + { QPSK, FEC_5_6, 11000, 14000}, + { QPSK, FEC_7_8, 12000, 15000}, +}; + +static struct vidtv_demod_cnr_to_qual_s vidtv_demod_s2_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QPSK, FEC_1_2, 9000, 12000}, + { QPSK, FEC_2_3, 11000, 14000}, + { QPSK, FEC_3_4, 12000, 15000}, + { QPSK, FEC_5_6, 12000, 15000}, + { QPSK, FEC_8_9, 13000, 16000}, + { QPSK, FEC_9_10, 13500, 16500}, + { PSK_8, FEC_2_3, 14500, 17500}, + { PSK_8, FEC_3_4, 16000, 19000}, + { PSK_8, FEC_5_6, 17500, 20500}, + { PSK_8, FEC_8_9, 19000, 22000}, +}; + +static struct vidtv_demod_cnr_to_qual_s vidtv_demod_t_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db*/ + { QPSK, FEC_1_2, 4100, 5900}, + { QPSK, FEC_2_3, 6100, 9600}, + { QPSK, FEC_3_4, 7200, 12400}, + { QPSK, FEC_5_6, 8500, 15600}, + { QPSK, FEC_7_8, 9200, 17500}, + + { QAM_16, FEC_1_2, 9800, 11800}, + { QAM_16, FEC_2_3, 12100, 15300}, + { QAM_16, FEC_3_4, 13400, 18100}, + { QAM_16, FEC_5_6, 14800, 21300}, + { QAM_16, FEC_7_8, 15700, 23600}, + + { QAM_64, FEC_1_2, 14000, 16000}, + { QAM_64, FEC_2_3, 19900, 25400}, + { QAM_64, FEC_3_4, 24900, 27900}, + { QAM_64, FEC_5_6, 21300, 23300}, + { QAM_64, FEC_7_8, 22000, 24000}, +}; + +static struct vidtv_demod_cnr_to_qual_s +*vidtv_match_cnr_s(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c; + struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; + u32 array_size = 0; + u32 i; + + c = &fe->dtv_property_cache; + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + cnr2qual = vidtv_demod_t_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_t_cnr_2_qual); + break; + + case SYS_DVBS: + cnr2qual = vidtv_demod_s_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_s_cnr_2_qual); + break; + + case SYS_DVBS2: + cnr2qual = vidtv_demod_s2_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_s2_cnr_2_qual); + break; + + case SYS_DVBC_ANNEX_A: + cnr2qual = vidtv_demod_c_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_c_cnr_2_qual); + break; + + default: + pr_warn_ratelimited("%s: unsupported delivery system: %u\n", + __func__, + c->delivery_system); + break; + } + + for (i = 0; i <= array_size; i++) + if (cnr2qual[i].modulation == c->modulation && + cnr2qual[i].fec == c->fec_inner) + return &cnr2qual[i]; + + return NULL; /* not found */ +} + +static void vidtv_demod_poll_snr_handler(struct work_struct *work) +{ + /* + * periodically check the signal quality and eventually + * lose the TS lock if it dips too low + */ + struct vidtv_demod_state *state; + struct dtv_frontend_properties *c; + struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; + struct vidtv_demod_config *config; + u16 snr = 0; + + state = container_of(work, struct vidtv_demod_state, poll_snr.work); + c = &state->frontend.dtv_property_cache; + config = &state->config; + + if (!state->frontend.ops.tuner_ops.get_rf_strength) + return; + + state->frontend.ops.tuner_ops.get_rf_strength(&state->frontend, &snr); + + cnr2qual = vidtv_match_cnr_s(&state->frontend); + if (!cnr2qual) + return; + + if (snr < cnr2qual->cnr_ok) { + /* eventually lose the TS lock */ + if (prandom_u32_max(100) < config->drop_tslock_prob_on_low_snr) + state->status = 0; + } else { + /* recover if the signal improves */ + if (prandom_u32_max(100) < + config->recover_tslock_prob_on_good_snr) + state->status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + + schedule_delayed_work(&state->poll_snr, + msecs_to_jiffies(POLL_THRD_TIME)); +} + +static int vidtv_demod_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + + *status = state->status; + + return 0; +} + +static int vidtv_demod_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int vidtv_demod_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + *strength = 0; + return 0; +} + +static int vidtv_demod_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int vidtv_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if it actually reads something from the hardware. + * Also, it should check for the locks, in order to avoid report wrong data + * to userspace. + */ +static int vidtv_demod_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + return 0; +} + +static int vidtv_demod_set_frontend(struct dvb_frontend *fe) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; + u32 tuner_status = 0; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + + /* store the CNR returned by the tuner */ + fe->ops.tuner_ops.get_rf_strength(fe, &state->tuner_cnr); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + state->status = (state->tuner_cnr > 0) ? FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK : + 0; + cnr2qual = vidtv_match_cnr_s(fe); + + /* signal isn't good: might lose the lock eventually */ + if (tuner_status == TUNER_STATUS_LOCKED && + state->tuner_cnr < cnr2qual->cnr_good) { + schedule_delayed_work(&state->poll_snr, + msecs_to_jiffies(POLL_THRD_TIME)); + + state->poll_snr_thread_running = true; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int vidtv_demod_sleep(struct dvb_frontend *fe) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + + if (state->poll_snr_thread_running) { + cancel_delayed_work_sync(&state->poll_snr); + state->poll_snr_thread_running = false; + state->poll_snr_thread_restart = true; + } + return 0; +} + +static int vidtv_demod_init(struct dvb_frontend *fe) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + u32 tuner_status = 0; + + if (state->cold_start) + INIT_DELAYED_WORK(&state->poll_snr, + &vidtv_demod_poll_snr_handler); + + /* + * At resume, start the snr poll thread only if it was suspended with + * the thread running. Extra care should be taken here, as some tuner + * status change might happen at resume time (for example, due to an + * ioctl syscall to set_frontend, or due to a release syscall). + */ + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (tuner_status == TUNER_STATUS_LOCKED && + state->poll_snr_thread_restart) { + schedule_delayed_work(&state->poll_snr, + msecs_to_jiffies(POLL_THRD_TIME)); + + state->poll_snr_thread_restart = false; + } + + state->cold_start = false; + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if the demod has support for DVB-S or DVB-S2 + */ +static int vidtv_demod_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if the demod has support for DVB-S or DVB-S2 + */ +static int vidtv_demod_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + return 0; +} + +static void vidtv_demod_release(struct dvb_frontend *fe) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + + if (state->poll_snr_thread_running) + cancel_delayed_work_sync(&state->poll_snr); + + kfree(state); +} + +static const struct dvb_frontend_ops vidtv_demod_ops = { + .delsys = { + SYS_DVBT, + SYS_DVBT2, + SYS_DVBC_ANNEX_A, + SYS_DVBS, + SYS_DVBS2, + }, + + .info = { + .name = "Dummy demod for DVB-T/T2/C/S/S2", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 62500, + .frequency_tolerance_hz = 29500 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_32 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO | + FE_CAN_INVERSION_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = vidtv_demod_release, + + .init = vidtv_demod_init, + .sleep = vidtv_demod_sleep, + + .set_frontend = vidtv_demod_set_frontend, + .get_frontend = vidtv_demod_get_frontend, + + .read_status = vidtv_demod_read_status, + .read_ber = vidtv_demod_read_ber, + .read_signal_strength = vidtv_demod_read_signal_strength, + .read_snr = vidtv_demod_read_snr, + .read_ucblocks = vidtv_demod_read_ucblocks, + + /* For DVB-S/S2 */ + .set_voltage = vidtv_demod_set_voltage, + .set_tone = vidtv_demod_set_tone, +}; + +static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { + {"vidtv_demod", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); + +static int vidtv_demod_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vidtv_demod_config *config = client->dev.platform_data; + struct vidtv_demod_state *state; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, + &vidtv_demod_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + /* return the pointer to the bridge driver */ + *config->frontend = &state->frontend; + i2c_set_clientdata(client, state); + + return 0; +} + +static int vidtv_demod_i2c_remove(struct i2c_client *client) +{ + struct vidtv_demod_state *state = i2c_get_clientdata(client); + + memset(&state->frontend.ops, 0, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = NULL; + kfree(state); + + return 0; +} + +static struct i2c_driver vidtv_demod_i2c_driver = { + .driver = { + .name = "vidtv_demod", + .suppress_bind_attrs = true, + }, + .probe = vidtv_demod_i2c_probe, + .remove = vidtv_demod_i2c_remove, + .id_table = vidtv_demod_i2c_id_table, +}; + +module_i2c_driver(vidtv_demod_i2c_driver); diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.h b/drivers/media/test-drivers/vidtv/vidtv_demod.h new file mode 100644 index 0000000000000..57d810f7a4462 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The Virtual DTV test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Written by Daniel W. S. Almeida + * Based on the example driver written by Emard + */ + +#ifndef VIDTV_DEMOD_H +#define VIDTV_DEMOD_H + +#include +#include + +struct vidtv_demod_cnr_to_qual_s { + /* attempt to use the same values as libdvbv5 */ + u32 modulation; + u32 fec; + u32 cnr_ok, cnr_good; +}; + +struct vidtv_demod_config { + struct dvb_frontend **frontend; + /* probability of losing the lock due to low snr */ + u8 drop_tslock_prob_on_low_snr; + /* probability of recovering when the signal improves */ + u8 recover_tslock_prob_on_good_snr; +}; + +struct vidtv_demod_state { + struct dvb_frontend frontend; + struct vidtv_demod_config config; + struct delayed_work poll_snr; + enum fe_status status; + u16 tuner_cnr; + bool cold_start; + bool poll_snr_thread_running; + bool poll_snr_thread_restart; +}; +#endif // VIDTV_DEMOD_H From patchwork Fri May 15 21:48:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Almeida X-Patchwork-Id: 209792 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 208FEC433E2 for ; Fri, 15 May 2020 21:49:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EC3D92073E for ; Fri, 15 May 2020 21:49:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="V6JQzLS5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728032AbgEOVtD (ORCPT ); Fri, 15 May 2020 17:49:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47188 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1726266AbgEOVtD (ORCPT ); Fri, 15 May 2020 17:49:03 -0400 Received: from mail-qt1-x842.google.com (mail-qt1-x842.google.com [IPv6:2607:f8b0:4864:20::842]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D05E5C061A0C; Fri, 15 May 2020 14:49:02 -0700 (PDT) Received: by mail-qt1-x842.google.com with SMTP id b1so3337778qtt.1; Fri, 15 May 2020 14:49:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=llFN1b4IyM2NmP1m0ZO+FT2673CkYgwiHGyYGvoTCac=; b=V6JQzLS5IPik6GglApB4QLQr3Rk+BCjB8x+3A9tOoC3C3imphXlKmhUgrKAZxavmyx q4qe66XDQCBdVO1q8/ssUlVuQTgZ0rqGgiqsnhzlcFQIVnmY9UcSOI6+3qqKnBnqC/R4 I7ntrrH1h1SDo7BQj/GX9PHIgd6mhuNRmcAuUDW8hNoZh9/kBHRToYXUUqJ0Fgygx4/Z gRAzFld1GON9AOXJ0hUTLYG/3Oos5EwISVfPUPLP28SyY1ImelHdCawfXHLpBj0DMi1/ e65Kh79Nips7EtVciqldL3mrDRpB3lY5qtxDyBMwj0zH8/Wp2+PgBNi3fMEDiTvEvhf1 x2Kw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=llFN1b4IyM2NmP1m0ZO+FT2673CkYgwiHGyYGvoTCac=; b=dtaBPAsfB1+chCm0m8tM5n9RSuPN8d3er1dEvF9oVRtt0EkBpJ5gGB8ZTV+XuRhBua vZdo8nG32gywxu2KYYOoQjiC9ju8fwdlM7VS4hfyehSo4mVi5+fFGg0awu/99YjgSp9B Tii8EzBt4kzZt53ggxZDv7hGDZ4Wi7DcN6gfsE9hOXo6Ah819pwB4YnK96xc9u1Etj6+ 4tKeeM4LlBLrzHhztZdI96GIf5XeRbhGORHAs8e1JpXvByJ/SQTmmOLTepdGNshyayuD gZ3lU1APPeVy4364DMCknt5UCsPlsH6qkrIr0IP9lSxT+67xfDFqTCUbeWNy2+dZJyQp tp1A== X-Gm-Message-State: AOAM532KurXtYwMaHN8kyorfzkrS3EVPlnuPkrGJpXtMvRmGPsszajs9 5HrGL2y1NQI/BJJwJ/ZqXnM= X-Google-Smtp-Source: ABdhPJyK6pyDmQBPYu9OmmQLKHe4TfmPSRCircJ4ofbXp6a1jaBRckDdpXWQS4OLcC874uTz7KE1hA== X-Received: by 2002:ac8:51c5:: with SMTP id d5mr5794502qtn.232.1589579341933; Fri, 15 May 2020 14:49:01 -0700 (PDT) Received: from localhost.localdomain ([2804:18:700e:2692:8980:dd13:6be6:4050]) by smtp.gmail.com with ESMTPSA id m21sm2495019qkh.50.2020.05.15.14.48.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2020 14:49:01 -0700 (PDT) From: "Daniel W. S. Almeida" X-Google-Original-From: Daniel W. S. Almeida To: mchehab+huawei@kernel.org, sean@mess.org, kstewart@linuxfoundation.org, allison@lohutok.net, tglx@linutronix.de Cc: "Daniel W. S. Almeida" , linux-media@vger.kernel.org, skhan@linuxfoundation.org, linux-kernel-mentees@lists.linuxfoundation.org, linux-kernel@vger.kernel.org Subject: [RFC, WIP, v5 05/10] media: vidtv: add wrappers for memcpy and memset Date: Fri, 15 May 2020 18:48:10 -0300 Message-Id: <20200515214815.15514-6-dwlsalmeida@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200515214815.15514-1-dwlsalmeida@gmail.com> References: <20200515214815.15514-1-dwlsalmeida@gmail.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: "Daniel W. S. Almeida" A lot of code in this driver is for serializing structures. This is error prone. Therefore, prevent buffer overflows by wrapping memcpy and memset, comparing the requested length against the buffer size. Signed-off-by: Daniel W. S. Almeida --- .../media/test-drivers/vidtv/vidtv_common.c | 86 +++++++++++++++++++ .../media/test-drivers/vidtv/vidtv_common.h | 27 ++++++ 2 files changed, 113 insertions(+) create mode 100644 drivers/media/test-drivers/vidtv/vidtv_common.c create mode 100644 drivers/media/test-drivers/vidtv/vidtv_common.h diff --git a/drivers/media/test-drivers/vidtv/vidtv_common.c b/drivers/media/test-drivers/vidtv/vidtv_common.c new file mode 100644 index 0000000000000..944f77625c12c --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_common.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Written by Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include +#include +#include +#include + +#include "vidtv_common.h" + +/** + * vidtv_memcpy() - wrapper routine to be used by MPEG-TS + * generator, in order to avoid going past the + * output buffer. + * @to: Starting element to where a MPEG-TS packet will + * be copied. + * @to_offset: Starting position of the @to buffer to be filled. + * @to_size: Size of the @to buffer. + * @from: Starting element of the buffer to be copied. + * @len: Number of elements to be copy from @from buffer + * into @to+ @to_offset buffer. + * + * Note: + * Real digital TV demod drivers should not have memcpy + * wrappers. We use it here because emulating MPEG-TS + * generation at kernelspace requires some extra care. + * + * Return: + * Returns the number of bytes written + */ +u32 vidtv_memcpy(void *to, + size_t to_offset, + size_t to_size, + const void *from, + size_t len) +{ + if (unlikely(to_offset + len > to_size)) { + pr_err_ratelimited("overflow detected, skipping. Try increasing the buffer size\n"); + return 0; + } + + memcpy(to + to_offset, from, len); + return len; +} + +/** + * vidtv_memset() - wrapper routine to be used by MPEG-TS + * generator, in order to avoid going past the + * output buffer. + * @to: Starting element to set + * @to_offset: Starting position of the @to buffer to be filled. + * @to_size: Size of the @to buffer. + * @from: Starting element of the buffer to be copied. + * @ten: Number of elements to be copy from @from buffer + * into @to+ @to_offset buffer. + * + * Note: + * Real digital TV demod drivers should not have memset + * wrappers. We use it here because emulating MPEG-TS + * generation at kernelspace requires some extra care. + * + * Return: + * Returns the number of bytes written + */ +u32 vidtv_memset(void *to, + size_t to_offset, + size_t to_size, + const int c, + size_t len) +{ + if (unlikely(to_offset + len > to_size)) { + pr_err_ratelimited("overflow detected, skipping. Try increasing the buffer size\n"); + return 0; + } + + memset(to + to_offset, c, len); + return len; +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_common.h b/drivers/media/test-drivers/vidtv/vidtv_common.h new file mode 100644 index 0000000000000..a3cb303cc7423 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_common.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Written by Daniel W. S. Almeida + */ + +#ifndef VIDTV_COMMON_H +#define VIDTV_COMMON_H + +#include + +u32 vidtv_memcpy(void *to, + size_t to_offset, + size_t to_size, + const void *from, + size_t len); + +u32 vidtv_memset(void *to, + size_t to_offset, + size_t to_size, + int c, + size_t len); + +#endif // VIDTV_COMMON_H From patchwork Fri May 15 21:48:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Almeida X-Patchwork-Id: 209791 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8F60AC433DF for ; Fri, 15 May 2020 21:49:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 52AEE2073E for ; Fri, 15 May 2020 21:49:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RnFUwQc1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728079AbgEOVtO (ORCPT ); Fri, 15 May 2020 17:49:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47222 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1728070AbgEOVtN (ORCPT ); Fri, 15 May 2020 17:49:13 -0400 Received: from mail-qv1-xf44.google.com (mail-qv1-xf44.google.com [IPv6:2607:f8b0:4864:20::f44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F1A1C061A0C; Fri, 15 May 2020 14:49:13 -0700 (PDT) Received: by mail-qv1-xf44.google.com with SMTP id r3so1887143qve.1; Fri, 15 May 2020 14:49:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=7nxgsR7Of2lFkTYJia5RssDEK3tM6CxHiQ2XWG4yU0w=; b=RnFUwQc1+nvp/0fBQTPuXrvTiMCHdB39KFouXzMXQi8wifPbsDiiT4xGpNOP9Ii1ox tHlz18jBOnk3bjOCJ1lPOawgh5urG4IsgO8ezOJh/Gy8Rplg3YA1xWzgfnroayPQ2Q4v BbMlIb/Ro85ctamcFaFzW2WdozV5LbZFI6H5zTJKJps/v/aJJPpEQi3HLGJSBsQovcYD IJfQokRjKkiQg19/Tmr8lgM5fGtZF2B6YF9Rkn7KFJMxVre+L+uIe6S3fgy/M7ZGwbMn aLRdMpipqHw35Uvl8bVzsFb9ic76+1mhKy/N8z1Yzj7ArPQH9tABT7UYmiHrhE11LDPU eCUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7nxgsR7Of2lFkTYJia5RssDEK3tM6CxHiQ2XWG4yU0w=; b=lOjUNTrIrJh1hZrGgWYO8mTpaTVAXo75Y/skDbLO3W+j+jSix+AF+b29c5F0gA75sJ nQEBIsOgRo/Uxjxypgo5M8t1fyqbnC1HjuT3Iu/Bt4b5n8sxct17EI7VheQTJ+qRhjIe VJEgd1ZobReFg2E6m8sVJgwG9gSgvMuCQHqsheomDajJoKojKLP7UMalYxtoKC1o3h61 527dbkLUEhFZRKS0oe7Nks8oLEZShci/qdEZALQ/+Pd0Z7sJ3EU72zE+A460+kUYSxJ7 UE6Nj8hmUoNicBwDCSBMXleOGNycc9wI8QXA1vUFiTQBgHwO+EkV+UleL2qF4zcMfOYX zQdQ== X-Gm-Message-State: AOAM5320zwERpSTmSSIvVP1GwW7ztR4FSYPwDKo30gq95ESJZP7Tsob4 EGlYoJJz0SwrTS+50oWaPkE= X-Google-Smtp-Source: ABdhPJwp/ouzUBSUC7ts3Tofa7ZXjU3hsoKUcOrY6N2LxAooDMRxkRfPxcfw7ShnFdwHiHD1tb09MQ== X-Received: by 2002:a05:6214:1ec:: with SMTP id c12mr5759599qvu.11.1589579351957; Fri, 15 May 2020 14:49:11 -0700 (PDT) Received: from localhost.localdomain ([2804:18:700e:2692:8980:dd13:6be6:4050]) by smtp.gmail.com with ESMTPSA id m21sm2495019qkh.50.2020.05.15.14.49.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2020 14:49:11 -0700 (PDT) From: "Daniel W. S. Almeida" X-Google-Original-From: Daniel W. S. Almeida To: mchehab+huawei@kernel.org, sean@mess.org, kstewart@linuxfoundation.org, allison@lohutok.net, tglx@linutronix.de Cc: "Daniel W. S. Almeida" , linux-media@vger.kernel.org, skhan@linuxfoundation.org, linux-kernel-mentees@lists.linuxfoundation.org, linux-kernel@vger.kernel.org Subject: [RFC, WIP, v5 07/10] media: vidtv: implement a PSI generator Date: Fri, 15 May 2020 18:48:12 -0300 Message-Id: <20200515214815.15514-8-dwlsalmeida@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200515214815.15514-1-dwlsalmeida@gmail.com> References: <20200515214815.15514-1-dwlsalmeida@gmail.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: "Daniel W. S. Almeida" PSI packets contain general information about a MPEG Transport Stream. A PSI generator is needed so userspace apps can retrieve information about the Transport Stream and eventually tune into a (dummy) channel. Because the generator is implemented in a separate file, it can be reused elsewhere in the media subsystem. Currently this commit adds support for working with 3 PSI tables: PAT, PMT and SDT. Signed-off-by: Daniel W. S. Almeida --- drivers/media/test-drivers/vidtv/vidtv_psi.c | 1037 ++++++++++++++++++ drivers/media/test-drivers/vidtv/vidtv_psi.h | 415 +++++++ 2 files changed, 1452 insertions(+) create mode 100644 drivers/media/test-drivers/vidtv/vidtv_psi.c create mode 100644 drivers/media/test-drivers/vidtv/vidtv_psi.h diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c new file mode 100644 index 0000000000000..37b1ecab5ee79 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c @@ -0,0 +1,1037 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains the logic to work with MPEG Program-Specific Information. + * These are defined both in ISO/IEC 13818-1 (systems) and ETSI EN 300 468. + * PSI is carried in the form of table structures, and although each table might + * technically be broken into one or more sections, we do not do this here, + * hence 'table' and 'section' are interchangeable for us. + * + * This code currently supports three tables: PAT, PMT and SDT. These are the + * bare minimum to get userspace to recognize our MPEG transport stream. It can + * be extended to support more PSI tables in the future. + * + * A note on endianness: MPEG layout is big-endian, therefore: + * - All fields spanning more than a byte must undergo 'cpu_to_beXX()' + * - All bitfields must have their ordering reversed if + * __LITTLE_ENDIAN_BITFIELD is defined. + * + * Written by: Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include + +#include "vidtv_psi.h" +#include "vidtv_common.h" +#include "vidtv_ts.h" + +#define CRC_SIZE_IN_BYTES 4 + +static u32 +vidtv_psi_ts_psi_write_into(struct psi_write_args args) +{ + /* + * Packetize PSI sections into TS packets: + * push a TS header (4bytes) every 184 bytes + * manage the continuity_counter + * add stuffing after the CRC + */ + + u32 nbytes_past_boundary = (args.dest_offset % TS_PACKET_LEN); + bool aligned = nbytes_past_boundary == 0; + + /* + * whether we need to fragment the data into multiple ts packets + * if we are aligned we need to spare one byte for the pointer_field + */ + bool split = (aligned) ? + args.len > TS_PAYLOAD_LEN - 1 : + nbytes_past_boundary + args.len > TS_PACKET_LEN; + + /* how much we can write in this packet */ + u32 payload_write_len = (split) ? + (aligned) ? TS_PAYLOAD_LEN : + TS_PACKET_LEN - nbytes_past_boundary : + args.len; + + struct psi_write_args new_args = {0}; + struct vidtv_mpeg_ts ts_header = {0}; + + u32 nbytes = 0; /* number of bytes written by this function */ + u32 temp = 0; + + /* Just a sanity check, should not really happen because we stuff + * the packet when we finish a section, i.e. when we write the crc at + * the end. But if this happens then we have messed up the logic + * somewhere. + */ + if (args.new_psi_section && !aligned) { + pr_warn_ratelimited("Cannot write a new PSI section in a misaligned buffer\n"); + + /* forcibly align and hope for the best */ + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - nbytes_past_boundary); + + aligned = true; + } + + if (aligned) { + /* if at a packet boundary, write a new TS header */ + ts_header.sync_byte = TS_SYNC_BYTE; + ts_header.tei = 0; + ts_header.payload_start = 1; + ts_header.pid = args.pid; + ts_header.priority = 0; + ts_header.scrambling = 0; + ts_header.continuity_counter = *args.continuity_counter; + ts_header.payload = 1; + /* no adaptation field */ + ts_header.adaptation_field = 0; + + /* copy the header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + &ts_header, + sizeof(ts_header)); + + /* + * increment the countinuity counter since we have started a new + * packet + * + * This will trigger a discontinuity if the buffer is full, + * which is what we want: the continuity counter will be + * incremented but nothing will get copied by + * vidtv_memcpy/vidtv_memset, effectively dropping the packet. + */ + vidtv_ts_inc_cc(args.continuity_counter); + } + + if (args.new_psi_section) { + /* write the pointer_field in the first byte of the payload */ + temp = vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + 0x0, + 1); + /* one byte was used by the pointer field*/ + nbytes += temp; + if (payload_write_len == TS_PAYLOAD_LEN) + payload_write_len -= temp; + } + + /* write as much of the payload as we possibly can */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + args.from, + payload_write_len); + + if (split) { + /* 'payload_write_len' written from a total of 'len' requested*/ + args.len -= payload_write_len; + /* + * recursively write the rest of the data until we do not + * need to split it anymore + */ + memcpy(&new_args, &args, sizeof(struct psi_write_args)); + new_args.from = args.from + payload_write_len; + new_args.dest_offset = args.dest_offset + nbytes; + new_args.new_psi_section = false; + + nbytes += vidtv_psi_ts_psi_write_into(new_args); + } + + /* + * as the CRC is last in the section, stuff the rest of the + * packet if there is any remaining space in there + */ + if (args.is_crc) + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - payload_write_len); + + return nbytes; +} + +static u32 table_section_crc32_write_into(struct crc32_write_args args) +{ + /* the CRC is the last entry in the section */ + u32 nbytes = 0; + __be32 crc; + struct psi_write_args psi_args = {0}; + + crc = cpu_to_be32(crc32_be(0, args.dest_buf, args.dest_offset)); + + psi_args.dest_buf = args.dest_buf; + psi_args.from = &crc; + psi_args.len = CRC_SIZE_IN_BYTES; + psi_args.dest_offset = args.dest_offset; + psi_args.pid = args.pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = true; + psi_args.dest_buf_sz = args.dest_buf_sz; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + return nbytes; +} + +struct vidtv_psi_desc *vidtv_psi_desc_init(struct vidtv_psi_desc *head, + u8 type, + u8 length) +{ + struct vidtv_psi_desc *desc; + + /* alloc enough memory for the flexible array too */ + desc = kzalloc(sizeof(*desc) + length, GFP_KERNEL); + + desc->type = type; + desc->length = length; + + if (head) { + while (head->next) + head = head->next; + + head->next = desc; + } + + return desc; +} + +void vidtv_psi_desc_destroy(struct vidtv_psi_desc *desc) +{ + struct vidtv_psi_desc *curr = desc; + struct vidtv_psi_desc *tmp = NULL; + + while (curr) { + tmp = curr; + curr = curr->next; + kfree(tmp); + } +} + +static u32 +vidtv_psi_desc_comp_len(struct vidtv_psi_desc *desc) +{ + u32 length = 0; + + if (!desc) + return 0; + + while (desc) { + length += desc->length; + desc = desc->next; + } + + return length; +} + +void vidtv_psi_desc_assign(struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc) +{ + /* + * Caller must recompute the section length afterwards at some point + * This function transfers ownedship of desc. + * Start by cleaning the old data + */ + if (*to) + vidtv_psi_desc_destroy(*to); + + *to = desc; /* reassign pointer */ +} + +static u32 vidtv_psi_desc_write_into(struct desc_write_args args) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + struct psi_write_args psi_args = {0}; + + psi_args.dest_buf = args.dest_buf; + psi_args.from = args.desc; + + psi_args.len = sizeof_field(struct vidtv_psi_desc, type) + + sizeof_field(struct vidtv_psi_desc, length); + + psi_args.dest_offset = args.dest_offset; + psi_args.pid = args.pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.dest_buf_sz; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + /* move 'from' pointer to point to u8 data[] */ + psi_args.from = args.desc + + sizeof_field(struct vidtv_psi_desc, type) + + sizeof_field(struct vidtv_psi_desc, length) + + sizeof(struct vidtv_psi_desc *); + + psi_args.len = args.desc->length; + psi_args.dest_offset = args.dest_offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + return nbytes; +} + +static u32 +vidtv_psi_table_header_write_into(struct header_write_args args) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + struct psi_write_args psi_args = {0}; + + psi_args.dest_buf = args.dest_buf; + psi_args.from = args.h; + psi_args.len = sizeof(struct vidtv_psi_table_header); + psi_args.dest_offset = args.dest_offset; + psi_args.pid = args.pid; + psi_args.new_psi_section = true; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.dest_buf_sz; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + return nbytes; +} + +void +vidtv_psi_pat_table_comp_sec_len(struct vidtv_psi_table_pat *pat) +{ + /* see ISO/IEC 13818-1 : 2000 p.43 */ + u16 length = 0; + u32 i; + + /* from immediately after 'section_length' until 'last_section_number'*/ + length += PAT_LEN_UNTIL_LAST_SECTION_NUMBER; + + /* do not count the pointer */ + for (i = 0; i < pat->programs; ++i) + length += sizeof(struct vidtv_psi_table_pat_program) - + sizeof(struct vidtv_psi_table_pat_program *); + + length += CRC_SIZE_IN_BYTES; + + if (length > MAX_SECTION_LEN) + pr_warn_ratelimited("len %d > %d\n", length, MAX_SECTION_LEN); + + pat->header.section_length = length; +} + +void vidtv_psi_pmt_table_comp_sec_len(struct vidtv_psi_table_pmt *pmt) +{ + /* see ISO/IEC 13818-1 : 2000 p.46 */ + u16 length = 0; + struct vidtv_psi_table_pmt_stream *s = pmt->stream; + + /* from immediately after 'section_length' until 'program_info_length'*/ + length += PMT_LEN_UNTIL_PROGRAM_INFO_LENGTH; + + pmt->desc_length = vidtv_psi_desc_comp_len(pmt->descriptor); + length += pmt->desc_length; + + while (s) { + /* skip both pointers at the end */ + length += sizeof(struct vidtv_psi_table_pmt_stream) - + sizeof(struct vidtv_psi_desc *) - + sizeof(struct vidtv_psi_table_pmt_stream *); + + s->desc_length = vidtv_psi_desc_comp_len(s->descriptor); + length += s->desc_length; + + s = s->next; + } + + length += CRC_SIZE_IN_BYTES; + + if (length > MAX_SECTION_LEN) + pr_warn_ratelimited("len %d > %d\n", length, MAX_SECTION_LEN); + + pmt->header.section_length = length; +} + +void vidtv_psi_sdt_table_comp_sec_len(struct vidtv_psi_table_sdt *sdt) +{ + /* see ETSI EN 300 468 V 1.10.1 p.24 */ + u16 length = 0; + struct vidtv_psi_table_sdt_service *s = sdt->service; + + /* + * from immediately after 'section_length' until + * 'reserved_for_future_use' + */ + length += SDT_LEN_UNTIL_RESERVED_FOR_FUTURE_USE; + + while (s) { + /* skip both pointers at the end */ + length += sizeof(struct vidtv_psi_table_sdt_service) - + sizeof(struct vidtv_psi_desc *) - + sizeof(struct vidtv_psi_table_sdt_service *); + + s->desc_length = vidtv_psi_desc_comp_len(s->descriptor); + length += s->desc_length; + + s = s->next; + } + + length += CRC_SIZE_IN_BYTES; + + if (length > MAX_SECTION_LEN) + pr_warn_ratelimited("len %d > %d\n", length, MAX_SECTION_LEN); + + sdt->header.section_length = length; +} + +struct vidtv_psi_table_pat_program* +vidtv_psi_pat_program_init(struct vidtv_psi_table_pat_program *head, + u16 service_id, + u16 pid) +{ + /* + * if 'head' is attached to a table, caller should recompute + * the section length afterwards at some point + */ + struct vidtv_psi_table_pat_program *program; + + program = kzalloc(sizeof(*program), GFP_KERNEL); + + program->service_id = cpu_to_be16(service_id); + /* pid for the PMT section in the TS */ + program->pid = pid; + program->next = NULL; + program->reserved = 0x7; + + if (head) { + while (head->next) + head = head->next; + + head->next = program; + } + + return program; +} + +void +vidtv_psi_pat_program_destroy(struct vidtv_psi_table_pat_program *p) +{ + struct vidtv_psi_table_pat_program *curr = p; + struct vidtv_psi_table_pat_program *tmp = NULL; + + while (curr) { + tmp = curr; + curr = curr->next; + kfree(tmp); + } +} + +void +vidtv_psi_pat_program_assign(struct vidtv_psi_table_pat *pat, + struct vidtv_psi_table_pat_program *p) +{ + /* This function transfers ownership of p to the table */ + + u16 program_count = 0; + struct vidtv_psi_table_pat_program *program = p; + struct vidtv_psi_table_pat_program *temp = pat->program; + + while (program) { + ++program_count; + program = program->next; + } + + pat->programs = program_count; + pat->program = p; + + /* Recompute section length */ + vidtv_psi_pat_table_comp_sec_len(pat); + + /* reassign if the new size is too big */ + if (pat->header.section_length > MAX_SECTION_LEN) + vidtv_psi_pat_program_assign(pat, temp); + else + vidtv_psi_pat_program_destroy(temp); +} + +void vidtv_psi_pat_table_init(struct vidtv_psi_table_pat *pat, + bool update_version_num, + u16 transport_stream_id) +{ + static u8 pat_version; + + pat->header.table_id = 0x0; + pat->header.syntax = 0x1; + pat->header.zero = 0x0; + pat->header.one = 0x03; + + /* transport stream ID, at will */ + pat->header.id = cpu_to_be16(transport_stream_id); + pat->header.current_next = 0x1; + + /* ETSI 300 468: indicates changes in the TS described by this table*/ + if (update_version_num) + ++pat_version; + + pat->header.version = pat_version; + + pat->header.one2 = 0x03; + pat->header.section_id = 0x0; + pat->header.last_section = 0x0; + + pat->programs = 0; + + vidtv_psi_pat_table_comp_sec_len(pat); +} + +u32 vidtv_psi_pat_write_into(char *buf, + u32 offset, + struct vidtv_psi_table_pat *pat, + u32 buf_sz, + u8 *continuity_counter) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + const u16 pat_pid = VIDTV_PAT_PID; + + struct vidtv_psi_table_pat_program *p = pat->program; + struct header_write_args h_args = {0}; + struct psi_write_args args = {0}; + struct crc32_write_args c_args = {0}; + + vidtv_psi_pat_table_comp_sec_len(pat); + + h_args.dest_buf = buf; + h_args.dest_offset = offset; + h_args.h = &pat->header; + h_args.pid = pat_pid; + h_args.continuity_counter = continuity_counter; + h_args.dest_buf_sz = buf_sz; + + nbytes += vidtv_psi_table_header_write_into(h_args); + + /* note that the field 'u16 programs' is not really part of the PAT */ + + args.dest_buf = buf; + args.pid = pat_pid; + args.new_psi_section = false; + args.continuity_counter = continuity_counter; + args.is_crc = false; + args.dest_buf_sz = buf_sz; + + while (p) { + /* copy the PAT programs */ + args.from = p; + /* skip the pointer */ + args.len = sizeof(*p) - + sizeof(struct vidtv_psi_table_pat_program *); + args.dest_offset = offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(args); + + p = p->next; + } + + c_args.dest_buf = buf; + c_args.dest_offset = offset + nbytes; + c_args.pid = pat_pid; + c_args.continuity_counter = continuity_counter; + c_args.dest_buf_sz = buf_sz; + + nbytes += table_section_crc32_write_into(c_args); + + return nbytes; +} + +void +vidtv_psi_pat_table_destroy(struct vidtv_psi_table_pat *p) +{ + vidtv_psi_pat_program_destroy(p->program); +} + +struct vidtv_psi_table_pmt_stream* +vidtv_psi_pmt_stream_init(struct vidtv_psi_table_pmt_stream *head, + enum vidtv_psi_stream_types stream_type, + u16 es_pid) +{ + struct vidtv_psi_table_pmt_stream *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + + stream->type = stream_type; + stream->elementary_pid = es_pid; + stream->reserved = 0x07; + + stream->desc_length = vidtv_psi_desc_comp_len(stream->descriptor); + + stream->zero = 0x0; + stream->reserved2 = 0x0f; + + if (head) { + while (head->next) + head = head->next; + + head->next = stream; + } + + return stream; +} + +void vidtv_psi_pmt_stream_destroy(struct vidtv_psi_table_pmt_stream *s) +{ + struct vidtv_psi_table_pmt_stream *curr_stream = s; + struct vidtv_psi_table_pmt_stream *tmp_stream = NULL; + + while (curr_stream) { + tmp_stream = curr_stream; + curr_stream = curr_stream->next; + kfree(tmp_stream); + } +} + +void vidtv_psi_pmt_stream_assign(struct vidtv_psi_table_pmt *pmt, + struct vidtv_psi_table_pmt_stream *s) +{ + /* This function transfers ownership of s to the table */ + struct vidtv_psi_table_pmt_stream *stream = s; + struct vidtv_psi_desc *desc = s->descriptor; + struct vidtv_psi_table_pmt_stream *temp = pmt->stream; + + while (stream) + stream = stream->next; + + while (desc) + desc = desc->next; + + pmt->stream = s; + /* Recompute section length */ + vidtv_psi_pmt_table_comp_sec_len(pmt); + + /* reassign if the new size is too big */ + if (pmt->header.section_length > MAX_SECTION_LEN) + vidtv_psi_pmt_stream_assign(pmt, temp); + else + vidtv_psi_pmt_stream_destroy(temp); +} + +u16 vidtv_psi_pmt_get_pid(struct vidtv_psi_table_pmt *section, + struct vidtv_psi_table_pat *pat) +{ + struct vidtv_psi_table_pat_program *program = pat->program; + + /* + * service_id is the same as program_number in the + * corresponding program_map_section + * see ETSI EN 300 468 v1.15.1 p. 24 + */ + while (program) { + if (program->service_id == section->header.id) + return pat->program->pid; + + program = program->next; + } + + return TS_LAST_VALID_PID + 1; /* not found */ +} + +void vidtv_psi_pmt_table_init(struct vidtv_psi_table_pmt *pmt, + bool update_version_num, + u16 program_number, + u16 pcr_pid) +{ + static u8 pmt_version; + + pmt->header.table_id = 0x2; + pmt->header.syntax = 0x1; + pmt->header.zero = 0x0; + pmt->header.one = 0x3; + + pmt->header.id = cpu_to_be16(program_number); + pmt->header.current_next = 0x1; + + /* ETSI 300 468: indicates changes in the TS described by this table*/ + if (update_version_num) + ++pmt_version; + + pmt->header.version = pmt_version; + + pmt->header.one2 = 0x3; + pmt->header.section_id = 0; + pmt->header.last_section = 0; + + pmt->pcr_pid = (pcr_pid) ? pcr_pid : 0x1fff; + pmt->reserved2 = 0x03; + + pmt->reserved3 = 0x0f; + pmt->zero3 = 0x0; + + pmt->desc_length = vidtv_psi_desc_comp_len(pmt->descriptor); + + vidtv_psi_pmt_table_comp_sec_len(pmt); +} + +u32 vidtv_psi_pmt_write_into(char *buf, + u32 offset, + struct vidtv_psi_table_pmt *pmt, + u16 pid, + u32 buf_sz, + u8 *continuity_counter) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + struct vidtv_psi_desc *table_descriptor = pmt->descriptor; + struct vidtv_psi_table_pmt_stream *stream = pmt->stream; + struct vidtv_psi_desc *stream_descriptor = (stream) ? + pmt->stream->descriptor : + NULL; + + struct header_write_args h_args = {0}; + struct psi_write_args args = {0}; + struct desc_write_args d_args = {0}; + struct crc32_write_args c_args = {0}; + + vidtv_psi_pmt_table_comp_sec_len(pmt); + + h_args.dest_buf = buf; + h_args.dest_offset = offset; + h_args.h = &pmt->header; + h_args.pid = pid; + h_args.continuity_counter = continuity_counter; + h_args.dest_buf_sz = buf_sz; + + nbytes += vidtv_psi_table_header_write_into(h_args); + + /* write the two bitfields */ + args.dest_buf = buf; + args.from = pmt + sizeof(struct vidtv_psi_table_header); + args.len = sizeof_field(struct vidtv_psi_table_pmt, bitfield) + + sizeof_field(struct vidtv_psi_table_pmt, bitfield2); + args.dest_offset = offset + nbytes; + args.pid = pid; + args.new_psi_section = false; + args.continuity_counter = continuity_counter; + args.is_crc = false; + args.dest_buf_sz = buf_sz; + + nbytes += vidtv_psi_ts_psi_write_into(args); + + while (table_descriptor) { + /* write the descriptors, if any */ + d_args.dest_buf = buf; + d_args.dest_offset = offset + nbytes; + d_args.desc = table_descriptor; + d_args.pid = pid; + d_args.continuity_counter = continuity_counter; + d_args.dest_buf_sz = buf_sz; + + nbytes += vidtv_psi_desc_write_into(d_args); + + table_descriptor = table_descriptor->next; + } + + while (stream) { + /* write the streams, if any */ + args.from = stream; + args.len = sizeof_field(struct vidtv_psi_table_pmt_stream, + type) + + sizeof_field(struct vidtv_psi_table_pmt_stream, + bitfield) + + sizeof_field(struct vidtv_psi_table_pmt_stream, + bitfield2); + args.dest_offset = offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(args); + + while (stream_descriptor) { + /* write the stream descriptors, if any */ + d_args.desc = stream_descriptor; + d_args.dest_offset = offset + nbytes; + + nbytes += vidtv_psi_desc_write_into(d_args); + + stream_descriptor = stream_descriptor->next; + } + + stream = stream->next; + } + + c_args.dest_buf = buf; + c_args.dest_offset = offset + nbytes; + c_args.pid = pid; + c_args.continuity_counter = continuity_counter; + c_args.dest_buf_sz = buf_sz; + + nbytes += table_section_crc32_write_into(c_args); + + return nbytes; +} + +void vidtv_psi_pmt_table_destroy(struct vidtv_psi_table_pmt *pmt) +{ + struct vidtv_psi_desc *curr_desc = pmt->descriptor; + struct vidtv_psi_desc *tmp_desc = NULL; + + while (curr_desc) { + tmp_desc = curr_desc; + curr_desc = curr_desc->next; + vidtv_psi_desc_destroy(tmp_desc); + kfree(tmp_desc); + } + + vidtv_psi_pmt_stream_destroy(pmt->stream); +} + +void vidtv_psi_sdt_table_init(struct vidtv_psi_table_sdt *sdt, + bool update_version_num, + u16 transport_stream_id) +{ + static u8 sdt_version; + + sdt->header.table_id = 0x42; + + sdt->header.one = 0x3; + sdt->header.zero = 0x1; + /* + * The PAT, PMT, and CAT all set this to 0. + * Other tables set this to 1. + */ + sdt->header.syntax = 0x1; + + /* + * This is a 16-bit field which serves as a label for identification + * of the TS, about which the SDT informs, from any other multiplex + * within the delivery system. + */ + sdt->header.id = cpu_to_be16(transport_stream_id); + sdt->header.current_next = 0x1; + + /* ETSI 300 468: indicates changes in the TS described by this table*/ + if (update_version_num) + ++sdt_version; + + sdt->header.version = sdt_version; + + sdt->header.one2 = 0x3; + sdt->header.section_id = 0; + sdt->header.last_section = 0; + + sdt->network_id = cpu_to_be16(transport_stream_id); + sdt->reserved = 0xff; + + vidtv_psi_sdt_table_comp_sec_len(sdt); +} + +u32 vidtv_psi_sdt_write_into(char *buf, + u32 offset, + struct vidtv_psi_table_sdt *sdt, + u32 buf_sz, + u8 *continuity_counter) +{ + u32 nbytes = 0; /* the number of bytes written */ + u16 sdt_pid = VIDTV_SDT_PID; /* see ETSI EN 300 468 v1.15.1 p. 11 */ + + struct vidtv_psi_table_sdt_service *service = sdt->service; + struct vidtv_psi_desc *service_desc = (sdt->service) ? + sdt->service->descriptor : + NULL; + + struct header_write_args h_args = {0}; + struct psi_write_args args = {0}; + struct desc_write_args d_args = {0}; + struct crc32_write_args c_args = {0}; + + vidtv_psi_sdt_table_comp_sec_len(sdt); + + h_args.dest_buf = buf; + h_args.dest_offset = offset; + h_args.h = &sdt->header; + h_args.pid = sdt_pid; + h_args.continuity_counter = continuity_counter; + h_args.dest_buf_sz = buf_sz; + + nbytes += vidtv_psi_table_header_write_into(h_args); + + args.dest_buf = buf; + args.from = sdt + sizeof(struct vidtv_psi_table_header); + + args.len = sizeof_field(struct vidtv_psi_table_sdt, network_id) + + sizeof_field(struct vidtv_psi_table_sdt, reserved); + + args.dest_offset = offset + nbytes; + args.pid = sdt_pid; + args.new_psi_section = false; + args.continuity_counter = continuity_counter; + args.is_crc = false; + args.dest_buf_sz = buf_sz; + + /* copy u16 network_id + u8 reserved)*/ + nbytes += vidtv_psi_ts_psi_write_into(args); + + while (service) { + /* copy the services, if any */ + args.from = service; + /* skip both pointers at the end */ + args.len = sizeof(struct vidtv_psi_table_sdt_service) - + sizeof(struct vidtv_psi_desc *) - + sizeof(struct vidtv_psi_table_sdt_service *); + args.dest_offset = offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(args); + + while (service_desc) { + /* copy the service descriptors, if any */ + d_args.dest_buf = buf; + d_args.dest_offset = offset + nbytes; + d_args.desc = service_desc; + d_args.pid = sdt_pid; + d_args.continuity_counter = continuity_counter; + d_args.dest_buf_sz = buf_sz; + + nbytes += vidtv_psi_desc_write_into(d_args); + + service_desc = service_desc->next; + } + + service = service->next; + } + + c_args.dest_buf = buf; + c_args.dest_offset = offset + nbytes; + c_args.pid = sdt_pid; + c_args.continuity_counter = continuity_counter; + c_args.dest_buf_sz = buf_sz; + + nbytes += table_section_crc32_write_into(c_args); + + return nbytes; +} + +void vidtv_psi_sdt_table_destroy(struct vidtv_psi_table_sdt *sdt) +{ + struct vidtv_psi_table_sdt_service *curr_service = sdt->service; + struct vidtv_psi_table_sdt_service *tmp_service = NULL; + struct vidtv_psi_desc *curr_desc = (sdt->service) ? + sdt->service->descriptor : NULL; + struct vidtv_psi_desc *tmp_desc = NULL; + + while (curr_service) { + curr_desc = curr_service->descriptor; + + while (curr_desc) { + /* clear all descriptors for the service */ + tmp_desc = curr_desc; + curr_desc = curr_desc->next; + vidtv_psi_desc_destroy(tmp_desc); + kfree(tmp_desc); + } + + /* then clear the current service */ + tmp_service = curr_service; + curr_service = curr_service->next; + kfree(tmp_service); + } +} + +struct vidtv_psi_table_sdt_service +*vidtv_psi_sdt_service_init(struct vidtv_psi_table_sdt_service *head, + u16 service_id) +{ + /* + * if 'head' is attached to a table, caller should recompute + * the section length afterwards at some point + */ + struct vidtv_psi_table_sdt_service *service; + + service = kzalloc(sizeof(*service), GFP_KERNEL); + + /* + * ETSI 300 468: this is a 16bit field which serves as a label to + * identify this service from any other service within the TS. + * The service id is the same as the program number in the + * corresponding program_map_section + */ + service->service_id = cpu_to_be16(service_id); + service->EIT_schedule = 0x0; + service->EIT_present_following = 0x0; + service->reserved = 0x3f; /* all bits on */ + service->free_CA_mode = 0x0; /* not scrambled */ + service->running_status = RUNNING; + + if (head) { + while (head->next) + head = head->next; + + head->next = service; + } + + return service; +} + +void +vidtv_psi_sdt_service_destroy(struct vidtv_psi_table_sdt_service *service) +{ + struct vidtv_psi_table_sdt_service *curr = service; + struct vidtv_psi_table_sdt_service *tmp = NULL; + + while (curr) { + tmp = curr; + curr = curr->next; + kfree(tmp); + } +} + +void +vidtv_psi_sdt_service_assign(struct vidtv_psi_table_sdt *sdt, + struct vidtv_psi_table_sdt_service *service) +{ + struct vidtv_psi_table_sdt_service *temp = sdt->service; + + sdt->service = service; + + /* recompute section length */ + vidtv_psi_sdt_table_comp_sec_len(sdt); + + /* reassign if the new size is too big */ + if (sdt->header.section_length > MAX_SECTION_LEN) + vidtv_psi_sdt_service_assign(sdt, temp); + else + vidtv_psi_sdt_service_destroy(temp); +} + +void +vidtv_psi_pmt_create_sec_for_each_pat_entry(struct vidtv_psi_table_pat *pat, + struct vidtv_psi_table_pmt sec[]) + +{ + /* + * PMTs contain information about programs. For each program, + * there is one PMT section. This function will create a section + * for each program found in the PAT + */ + struct vidtv_psi_table_pat_program *program = pat->program; + u32 i = 0; + + while (program) { + vidtv_psi_pmt_table_init(&sec[i], + false, + be16_to_cpu(sec[i].header.id), + 0); + + ++i; + program = program->next; + } +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.h b/drivers/media/test-drivers/vidtv/vidtv_psi.h new file mode 100644 index 0000000000000..77b616a46757a --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.h @@ -0,0 +1,415 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file contains the logic to work with MPEG Program-Specific Information. + * These are defined both in ISO/IEC 13818-1 (systems) and ETSI EN 300 468. + * PSI is carried in the form of table structures, and although each table might + * technically be broken into one or more sections, we do not do this here, + * hence 'table' and 'section' are interchangeable for us. + * + * This code currently supports three tables: PAT, PMT and SDT. These are the + * bare minimum to get userspace to recognize our MPEG transport stream. It can + * be extended to support more PSI tables in the future. + * + * A note on endianness: MPEG layout is big-endian, therefore: + * - All fields spanning more than a byte must undergo 'cpu_to_beXX()' + * - All bitfields must have their ordering reversed if + * __LITTLE_ENDIAN_BITFIELD is defined. + * + * Written by: Daniel W. S. Almeida + */ + +#ifndef VIDTV_PSI_H +#define VIDTV_PSI_H + +#include +#include + +/* + * all section lengths start immediately after the 'section_length' field + * see ISO/IEC 13818-1 : 2000 and ETSI EN 300 468 V 1.10.1 for + * reference + */ +#define PAT_LEN_UNTIL_LAST_SECTION_NUMBER 5 +#define PMT_LEN_UNTIL_PROGRAM_INFO_LENGTH 9 +#define SDT_LEN_UNTIL_RESERVED_FOR_FUTURE_USE 8 +#define MAX_SECTION_LEN 1021 +#define VIDTV_PAT_PID 0 +#define VIDTV_SDT_PID 0x0011 + +enum vidtv_psi_descriptors { + REGISTRATION_DESCRIPTOR = 0x05, + SERVICE_DESCRIPTOR = 0x48, +}; + +enum vidtv_psi_stream_types { + /* see ISO/IEC 13818-1 2000 p. 48 */ + STREAM_PRIVATE_DATA = 0x06, +}; + +struct vidtv_psi_desc { + u8 type; + u8 length; + struct vidtv_psi_desc *next; + u8 data[]; +} __packed; + +struct vidtv_psi_desc_service { + u8 type; + u8 length; + struct vidtv_psi_desc *next; + + u8 service_type; + char *name; + char *name_emph; + char *provider; + char *provider_emph; +} __packed; + +struct vidtv_psi_desc_registration { + u8 type; + u8 length; + struct vidtv_psi_desc *next; + + /* + * The format_identifier is a 32-bit value obtained from a Registration + * Authority as designated by ISO/IEC JTC 1/SC 29. + */ + __be32 format_id; + /* + * The meaning of additional_identification_info bytes, if any, are + * defined by the assignee of that format_identifier, and once defined + * they shall not change. + */ + u8 additional_identification_info[]; +} __packed; + +struct vidtv_psi_table_header { + u8 table_id; + union { + __be16 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 section_length:12; + u8 one:2; + u8 zero:1; + u8 syntax:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 syntax:1; + u8 zero:1; + u8 one:2; + u16 section_length:12; +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + + __be16 id; /* TS ID */ +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 current_next:1; + u8 version:5; + u8 one2:2; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 one2:2; + u8 version:5; + u8 current_next:1; +#else +#error "Unknown bitfield ordering" +#endif + u8 section_id; /* section_number */ + u8 last_section; /* last_section_number */ +} __packed; + +struct vidtv_psi_table_pat_program { + __be16 service_id; + union { + __be16 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 reserved:3; + u16 pid:13; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 pid:13; + u8 reserved:3; +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + struct vidtv_psi_table_pat_program *next; +} __packed; + +struct vidtv_psi_table_pat { + struct vidtv_psi_table_header header; + u16 programs; + struct vidtv_psi_table_pat_program *program; +} __packed; + +struct vidtv_psi_table_sdt_service { + __be16 service_id; +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 EIT_present_following:1; + u8 EIT_schedule:1; + u8 reserved:6; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 reserved:6; + u8 EIT_schedule:1; + u8 EIT_present_following:1; +#else +#error "Unknown bitfield ordering" +#endif + union { + __be16 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 desc_length:12; + u16 free_CA_mode:1; + u16 running_status:3; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 running_status:3; + u16 free_CA_mode:1; + u16 desc_length:12; +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + struct vidtv_psi_desc *descriptor; + struct vidtv_psi_table_sdt_service *next; +} __packed; + +struct vidtv_psi_table_sdt { + struct vidtv_psi_table_header header; + __be16 network_id; /* original_network_id */ + u8 reserved; + struct vidtv_psi_table_sdt_service *service; +} __packed; + +enum service_running_status { + RUNNING, +}; + +enum service_type { + /* see ETSI EN 300 468 v1.15.1 p. 77 */ + DIGITAL_TELEVISION_SERVICE = 0x1, +}; + +struct vidtv_psi_table_pmt_stream { + u8 type; + union { + __be16 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 elementary_pid:13; + u16 reserved:3; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 reserved:3; + u16 elementary_pid:13; +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + union { + __be16 bitfield2; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 desc_length:10; + u16 zero:2; + u16 reserved2:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 reserved2:4; + u16 zero:2; + u16 desc_length:10; +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + struct vidtv_psi_desc *descriptor; + struct vidtv_psi_table_pmt_stream *next; +} __packed; + +struct vidtv_psi_table_pmt { + struct vidtv_psi_table_header header; + union { + __be16 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 pcr_pid:13; + u16 reserved2:3; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 reserved2:3; + u16 pcr_pid:13; +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + + union { + __be16 bitfield2; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 desc_length:10; /* program_info_length */ + u16 zero3:2; + u16 reserved3:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 reserved3:4; + u16 zero3:2; + u16 desc_length:10; /* program_info_length */ +#else +#error "Unknown bitfield ordering" +#endif + } __packed; + } __packed; + struct vidtv_psi_desc *descriptor; + struct vidtv_psi_table_pmt_stream *stream; +} __packed; + +/** + * struct psi_write_args - Arguments for the PSI packetizer + * @dest_buf: The buffer to write into. + * @from: PSI data to be copied. + * @len: How much to write. + * @dest_offset: where to start writing in the dest_buffer. + * @pid: TS packet ID. + * @new_psi_section: Set when starting a table section. + * @continuity_counter: Incremented on every new packet. + * @is_crc: Set when writing the CRC at the end. + * @dest_buf_sz: The size of the dest_buffer + */ +struct psi_write_args { + void *dest_buf; + void *from; + size_t len; + u32 dest_offset; + u16 pid; + bool new_psi_section; + u8 *continuity_counter; + bool is_crc; + u32 dest_buf_sz; +}; + +struct desc_write_args { + void *dest_buf; + u32 dest_offset; + struct vidtv_psi_desc *desc; + u16 pid; + u8 *continuity_counter; + u32 dest_buf_sz; +}; + +struct crc32_write_args { + void *dest_buf; + u32 dest_offset; + u16 pid; + u8 *continuity_counter; + u32 dest_buf_sz; +}; + +struct header_write_args { + void *dest_buf; + u32 dest_offset; + struct vidtv_psi_table_header *h; + u16 pid; + u8 *continuity_counter; + u32 dest_buf_sz; +}; + +struct vidtv_psi_desc *vidtv_psi_desc_init(struct vidtv_psi_desc *head, + u8 type, + u8 length); + +void vidtv_psi_pat_table_init(struct vidtv_psi_table_pat *pat, + bool update_version_num, + u16 transport_stream_id); + +struct vidtv_psi_table_pat_program* +vidtv_psi_pat_program_init(struct vidtv_psi_table_pat_program *head, + u16 service_id, + u16 pid); + +struct vidtv_psi_table_pmt_stream* +vidtv_psi_pmt_stream_init(struct vidtv_psi_table_pmt_stream *head, + enum vidtv_psi_stream_types stream_type, + u16 es_pid); + +void vidtv_psi_pmt_table_init(struct vidtv_psi_table_pmt *pmt, + bool update_version_num, + u16 program_number, + u16 pcr_pid); + +void +vidtv_psi_sdt_table_init(struct vidtv_psi_table_sdt *sdt, + bool update_version_num, + u16 transport_stream_id); + +struct vidtv_psi_table_sdt_service* +vidtv_psi_sdt_service_init(struct vidtv_psi_table_sdt_service *head, + u16 service_id); + +void +vidtv_psi_desc_destroy(struct vidtv_psi_desc *desc); + +void +vidtv_psi_pat_program_destroy(struct vidtv_psi_table_pat_program *p); + +void +vidtv_psi_pat_table_destroy(struct vidtv_psi_table_pat *p); + +void +vidtv_psi_pmt_stream_destroy(struct vidtv_psi_table_pmt_stream *s); + +void +vidtv_psi_pmt_table_destroy(struct vidtv_psi_table_pmt *pmt); + +void +vidtv_psi_sdt_table_destroy(struct vidtv_psi_table_sdt *sdt); + +void +vidtv_psi_sdt_service_destroy(struct vidtv_psi_table_sdt_service *service); + +void +vidtv_psi_sdt_service_assign(struct vidtv_psi_table_sdt *sdt, + struct vidtv_psi_table_sdt_service *service); + +void vidtv_psi_desc_assign(struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc); + +void vidtv_psi_pat_program_assign(struct vidtv_psi_table_pat *pat, + struct vidtv_psi_table_pat_program *p); + +void vidtv_psi_pmt_stream_assign(struct vidtv_psi_table_pmt *pmt, + struct vidtv_psi_table_pmt_stream *s); +void +vidtv_psi_pmt_create_sec_for_each_pat_entry(struct vidtv_psi_table_pat *pat, + struct vidtv_psi_table_pmt *sec); + +u16 vidtv_psi_pmt_get_pid(struct vidtv_psi_table_pmt *section, + struct vidtv_psi_table_pat *pat); + +void vidtv_psi_pat_table_comp_sec_len(struct vidtv_psi_table_pat *pat); +void vidtv_psi_pmt_table_comp_sec_len(struct vidtv_psi_table_pmt *pmt); +void vidtv_psi_sdt_table_comp_sec_len(struct vidtv_psi_table_sdt *sdt); + +u32 vidtv_psi_pat_write_into(char *buf, + u32 offset, + struct vidtv_psi_table_pat *pat, + u32 buf_sz, + u8 *continuity_counter); + +u32 vidtv_psi_sdt_write_into(char *buf, + u32 offset, + struct vidtv_psi_table_sdt *sdt, + u32 buf_sz, + u8 *continuity_counter); + +u32 vidtv_psi_pmt_write_into(char *buf, + u32 offset, + struct vidtv_psi_table_pmt *pmt, + u16 pid, + u32 buf_sz, + u8 *continuity_counter); + +#endif // VIDTV_PSI_H From patchwork Fri May 15 21:48:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Almeida X-Patchwork-Id: 209790 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3534C433E2 for ; Fri, 15 May 2020 21:49:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8D5BF2073E for ; Fri, 15 May 2020 21:49:30 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bTmMuV2f" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728068AbgEOVt0 (ORCPT ); Fri, 15 May 2020 17:49:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1726228AbgEOVtY (ORCPT ); Fri, 15 May 2020 17:49:24 -0400 Received: from mail-qk1-x743.google.com (mail-qk1-x743.google.com [IPv6:2607:f8b0:4864:20::743]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E5D2CC061A0C; Fri, 15 May 2020 14:49:23 -0700 (PDT) Received: by mail-qk1-x743.google.com with SMTP id n14so4205853qke.8; Fri, 15 May 2020 14:49:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=T6/BwcxRsNbxZY3jXSZZfthfw74QKU20kbzSNUFwfDo=; b=bTmMuV2fsGXzL3iw+KzCjZYZ9ScU5apzc7Osv1ZuL7qlmvKa/VTmLwbo5w8u2rpV6/ HJNzpXMFILjDVRRRnxwb8zdysmIg44KAdrzhXFwGS5zoP01RM/Lo8IYlawtaDDwKZ2SR uwkEeKsuRAtOOXMpJFFMbObThdrHnzTkYOsYux8pUHlZ5L7iLujnrOkUhgN+LUD3YcEh FbUFXeoRml9B1bk1jT/y6bHRLsNkx6fQhlpPnqCJgHzelydwyvQg0GrXQtyoDXu2jhmk dyXvbAbu+yPjIlI9xKYkQJyxnSQXXohWeWUwSzAf3NlhU/iUTUvVwAeht5QPQ2FvWx8e 4zXQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=T6/BwcxRsNbxZY3jXSZZfthfw74QKU20kbzSNUFwfDo=; b=cd4U0AT6EWH/X2HK+TVAj7w9+r8L4KLS7S4L1ncEh7HXZE1jWNHkSCC8Qv1fgx5opT uPaslKJmQcPm24re54qWn0yDbw5u1/GIPeWPZ5UxOCHbqOBSHLPDGUgYc9XFhCSpJKyZ 2HT+bTIeu4O6vcGGWCRuqsiFUrnV1ykoJqhLtQXHo1eAxEQR4CJ/iQOQ5qt+pmdai5QX he80KqfPqLIvutE9g0nOxH0jyO4/188eIhKpoCSKjV69bAUiZlrJPH7FlPHansElHUCN o1DA+h7BMrnjYIaP39H3Ub0FPviN+j00U9HQFnYCTKfnpDfWM9k+3hGIXTSiF5KQ8jKC mzSw== X-Gm-Message-State: AOAM533JqDFgMApHW4DpKtWE8UfNnBQVcJPXGXLnokXSgF2x1BtDkaqm aDTtC2JR+EpKAtgbcioKSj8= X-Google-Smtp-Source: ABdhPJyF2Lo6nmg+xL/Rq51Vez0zfFtGlQf6kJn5bWApHuUwiyFtASG13rYtD49i6ctC1mXc8yH/Eg== X-Received: by 2002:a05:620a:128a:: with SMTP id w10mr5672799qki.70.1589579362758; Fri, 15 May 2020 14:49:22 -0700 (PDT) Received: from localhost.localdomain ([2804:18:700e:2692:8980:dd13:6be6:4050]) by smtp.gmail.com with ESMTPSA id m21sm2495019qkh.50.2020.05.15.14.49.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2020 14:49:22 -0700 (PDT) From: "Daniel W. S. Almeida" X-Google-Original-From: Daniel W. S. Almeida To: mchehab+huawei@kernel.org, sean@mess.org, kstewart@linuxfoundation.org, allison@lohutok.net, tglx@linutronix.de Cc: "Daniel W. S. Almeida" , linux-media@vger.kernel.org, skhan@linuxfoundation.org, linux-kernel-mentees@lists.linuxfoundation.org, linux-kernel@vger.kernel.org Subject: [RFC, WIP, v5 09/10] media: vidtv: Implement a SMPTE 302M encoder Date: Fri, 15 May 2020 18:48:14 -0300 Message-Id: <20200515214815.15514-10-dwlsalmeida@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200515214815.15514-1-dwlsalmeida@gmail.com> References: <20200515214815.15514-1-dwlsalmeida@gmail.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: "Daniel W. S. Almeida" Implement a S302M encoder to make it possible to insert PCM audio data in the generated MPEG Transport Stream. This shall enable passing an audio signal into userspace so it can be decoded and played by media software. Signed-off-by: Daniel W. S. Almeida --- .../media/test-drivers/vidtv/vidtv_common.h | 2 + .../media/test-drivers/vidtv/vidtv_encoder.h | 103 +++ .../media/test-drivers/vidtv/vidtv_s302m.c | 638 ++++++++++++++++++ .../media/test-drivers/vidtv/vidtv_s302m.h | 119 ++++ 4 files changed, 862 insertions(+) create mode 100644 drivers/media/test-drivers/vidtv/vidtv_encoder.h create mode 100644 drivers/media/test-drivers/vidtv/vidtv_s302m.c create mode 100644 drivers/media/test-drivers/vidtv/vidtv_s302m.h diff --git a/drivers/media/test-drivers/vidtv/vidtv_common.h b/drivers/media/test-drivers/vidtv/vidtv_common.h index 1a31973f3ac61..a0480825b23a9 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_common.h +++ b/drivers/media/test-drivers/vidtv/vidtv_common.h @@ -12,6 +12,8 @@ #include +#define CLOCK_UNIT_90KHZ 90000 + u32 vidtv_memcpy(void *to, size_t to_offset, size_t to_size, diff --git a/drivers/media/test-drivers/vidtv/vidtv_encoder.h b/drivers/media/test-drivers/vidtv/vidtv_encoder.h new file mode 100644 index 0000000000000..361e49c58c96e --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_encoder.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains a generic encoder type that can provide data for a stream + * + * Written by Daniel W. S. Almeida + */ + +#ifndef VIDTV_ENCODER_H +#define VIDTV_ENCODER_H + +#include + +enum vidtv_encoder_id { + /* add IDs here when implementing new encoders */ + S302M, +}; + +/** + * struct vidtv_encoder - A generic encoder type. + * @id: So we can cast to a concrete implementation when needed. + * @name: Usually the same as the stream name. + * @encoder_buf: The encoder internal buffer for the access units. + * @encoder_buf_sz: The encoder buffer size, in bytes + * @encoder_buf_offset: Our byte position in the encoder buffer. + * @sample_count: How many samples we have encoded in total. + * @previous_sample_count: The previous sample count, before clear() was + * called. + * @nunits: The number of access units ready. + * @pts: Array mapping pts[i] for AU[i]. + * @dts: Array mapping dts[i] for AU[i] + * @nbytes: Array mapping how many bytes were written per AU. + * @offsets: array keeping track of AU offsets in the buffer. + * @src_buf: The source of raw data to be encoded, encoder might set a + * default if null. + * @src_buf_offset: Our position in the source buffer. + * @video: Either video or audio. + * @ctx: Encoder-specific state. + * @stream_id: Examples: Audio streams (0xc0-0xdf), Video streams + * (0xe0-0xef). + * @es_id: The TS PID to use for the elementary stream in this encoder. + * @encode: Prepare enough AUs for the given amount of time. + * @clear: Clear the encoder output. + * @sync: Attempt to synchronize with this encoder. + * @sampling_rate_hz: The sampling rate (or fps, if video) used. + * @access_unit_capacity: Controls allocation for the arrays. + * @last_sample_cb: Called when the encoder runs out of data. The encoder + * might optionally wrap around the src data if this is not + * implemented. This is so the source can read data in a + * piecemeal fashion instead of having to + * provide it all at once. + * @destroy: Destroy this encoder, freeing allocated resources. + */ +struct vidtv_encoder { + enum vidtv_encoder_id id; + char *name; + + u8 *encoder_buf; + u32 encoder_buf_sz; + u32 encoder_buf_offset; + + u32 sample_count; + u32 previous_sample_count; + + u32 nunits; + u32 *samples_per_unit; + u64 *pts; + u64 *dts; + u32 *nbytes; + u32 *offsets; + + void *src_buf; + u32 src_buf_sz; + u32 src_buf_offset; + + bool video; + void *ctx; + + u16 stream_id; + + u16 es_pid; + + void *(*encode)(struct vidtv_encoder *e, u64 elapsed_time_usecs); + + u8 (*clear)(struct vidtv_encoder *e); + + struct vidtv_encoder *sync; + + u32 sampling_rate_hz; + + u8 access_unit_capacity; + + void (*last_sample_cb)(u32 sample_no); + + void (*destroy)(struct vidtv_encoder *e); + + struct vidtv_encoder *next; +}; + +#endif /* VIDTV_ENCODER_H */ diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.c b/drivers/media/test-drivers/vidtv/vidtv_s302m.c new file mode 100644 index 0000000000000..1b21a3a55dfed --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.c @@ -0,0 +1,638 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the code for an AES3 (also known as AES/EBU) encoder. + * It is based on EBU Tech 3250 and SMPTE 302M technical documents. + * + * This encoder currently supports 16bit AES3 subframes using 16bit signed + * integers. + * + * Note: AU stands for Access Unit, and AAU stands for Audio Access Unit + * + * Written by Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vidtv_s302m.h" +#include "vidtv_encoder.h" +#include "vidtv_common.h" + +#define S302M_SAMPLING_RATE_HZ 48000 + +/* see EBU Tech 3250 2004 clause 4 */ +#define CHANNEL_STATUS_BIT_LEN 92 + +#define S302M_2CHANNELS 0 +#define S302M_BITS_PER_SAMPLE_16 0 + +/* see preamble definition in EBU Tech 3250 2004 clause 2.4 */ +#define PREAMBLE_X_F0 0xe2 /* sub-frame 1 */ +#define PREAMBLE_Y_F0 0xe4 /* sub-frame 2 */ +#define PREAMBLE_Z_F0 0xe8 /* sub-frame 1 + block start */ +/* F0 and F1 refer to whether the F bit was set in the previous sub-frame */ +#define PREAMBLE_X_F1 0x1d /* sub-frame 1 */ +#define PREAMBLE_Y_F1 0X1b /* sub-frame 2 */ +#define PREAMBLE_Z_F1 0x17 /* sub-frame 1 + block start */ + +#define S302M_BLOCK_SZ 192 +#define S302M_SIN_LUT_SZ 1024 + +/* + * This buffer contains PCM audio samples of a sine wave, compatible with + * S302m + */ +static int s302m_sin_lut[S302M_SIN_LUT_SZ] = { + 0x8000, 0x80c9, 0x8192, 0x825b, 0x8324, 0x83ed, 0x84b6, 0x857e, + 0x8647, 0x8710, 0x87d9, 0x88a1, 0x896a, 0x8a32, 0x8afb, 0x8bc3, + 0x8c8b, 0x8d53, 0x8e1b, 0x8ee3, 0x8fab, 0x9072, 0x9139, 0x9201, + 0x92c7, 0x938e, 0x9455, 0x951b, 0x95e1, 0x96a7, 0x976d, 0x9833, + 0x98f8, 0x99bd, 0x9a82, 0x9b47, 0x9c0b, 0x9ccf, 0x9d93, 0x9e56, + 0x9f19, 0x9fdc, 0xa09f, 0xa161, 0xa223, 0xa2e5, 0xa3a6, 0xa467, + 0xa527, 0xa5e8, 0xa6a7, 0xa767, 0xa826, 0xa8e5, 0xa9a3, 0xaa61, + 0xab1f, 0xabdc, 0xac98, 0xad55, 0xae10, 0xaecc, 0xaf87, 0xb041, + 0xb0fb, 0xb1b5, 0xb26e, 0xb326, 0xb3de, 0xb496, 0xb54d, 0xb603, + 0xb6b9, 0xb76f, 0xb824, 0xb8d8, 0xb98c, 0xba3f, 0xbaf2, 0xbba4, + 0xbc56, 0xbd07, 0xbdb7, 0xbe67, 0xbf17, 0xbfc5, 0xc073, 0xc121, + 0xc1cd, 0xc279, 0xc325, 0xc3d0, 0xc47a, 0xc524, 0xc5cc, 0xc675, + 0xc71c, 0xc7c3, 0xc869, 0xc90f, 0xc9b3, 0xca57, 0xcafb, 0xcb9d, + 0xcc3f, 0xcce0, 0xcd81, 0xce20, 0xcebf, 0xcf5d, 0xcffb, 0xd097, + 0xd133, 0xd1ce, 0xd268, 0xd302, 0xd39a, 0xd432, 0xd4c9, 0xd55f, + 0xd5f5, 0xd689, 0xd71d, 0xd7b0, 0xd842, 0xd8d3, 0xd964, 0xd9f3, + 0xda82, 0xdb0f, 0xdb9c, 0xdc28, 0xdcb3, 0xdd3d, 0xddc7, 0xde4f, + 0xded7, 0xdf5d, 0xdfe3, 0xe068, 0xe0eb, 0xe16e, 0xe1f0, 0xe271, + 0xe2f1, 0xe370, 0xe3ee, 0xe46b, 0xe4e8, 0xe563, 0xe5dd, 0xe656, + 0xe6cf, 0xe746, 0xe7bc, 0xe831, 0xe8a6, 0xe919, 0xe98b, 0xe9fc, + 0xea6d, 0xeadc, 0xeb4a, 0xebb7, 0xec23, 0xec8e, 0xecf8, 0xed61, + 0xedc9, 0xee30, 0xee96, 0xeefa, 0xef5e, 0xefc1, 0xf022, 0xf083, + 0xf0e2, 0xf140, 0xf19d, 0xf1f9, 0xf254, 0xf2ae, 0xf307, 0xf35e, + 0xf3b5, 0xf40a, 0xf45f, 0xf4b2, 0xf504, 0xf555, 0xf5a5, 0xf5f3, + 0xf641, 0xf68d, 0xf6d8, 0xf722, 0xf76b, 0xf7b3, 0xf7fa, 0xf83f, + 0xf884, 0xf8c7, 0xf909, 0xf94a, 0xf989, 0xf9c8, 0xfa05, 0xfa41, + 0xfa7c, 0xfab6, 0xfaee, 0xfb26, 0xfb5c, 0xfb91, 0xfbc5, 0xfbf8, + 0xfc29, 0xfc59, 0xfc88, 0xfcb6, 0xfce3, 0xfd0e, 0xfd39, 0xfd62, + 0xfd89, 0xfdb0, 0xfdd5, 0xfdfa, 0xfe1d, 0xfe3e, 0xfe5f, 0xfe7e, + 0xfe9c, 0xfeb9, 0xfed5, 0xfeef, 0xff09, 0xff21, 0xff37, 0xff4d, + 0xff61, 0xff74, 0xff86, 0xff97, 0xffa6, 0xffb4, 0xffc1, 0xffcd, + 0xffd8, 0xffe1, 0xffe9, 0xfff0, 0xfff5, 0xfff9, 0xfffd, 0xfffe, + 0xffff, 0xfffe, 0xfffd, 0xfff9, 0xfff5, 0xfff0, 0xffe9, 0xffe1, + 0xffd8, 0xffcd, 0xffc1, 0xffb4, 0xffa6, 0xff97, 0xff86, 0xff74, + 0xff61, 0xff4d, 0xff37, 0xff21, 0xff09, 0xfeef, 0xfed5, 0xfeb9, + 0xfe9c, 0xfe7e, 0xfe5f, 0xfe3e, 0xfe1d, 0xfdfa, 0xfdd5, 0xfdb0, + 0xfd89, 0xfd62, 0xfd39, 0xfd0e, 0xfce3, 0xfcb6, 0xfc88, 0xfc59, + 0xfc29, 0xfbf8, 0xfbc5, 0xfb91, 0xfb5c, 0xfb26, 0xfaee, 0xfab6, + 0xfa7c, 0xfa41, 0xfa05, 0xf9c8, 0xf989, 0xf94a, 0xf909, 0xf8c7, + 0xf884, 0xf83f, 0xf7fa, 0xf7b3, 0xf76b, 0xf722, 0xf6d8, 0xf68d, + 0xf641, 0xf5f3, 0xf5a5, 0xf555, 0xf504, 0xf4b2, 0xf45f, 0xf40a, + 0xf3b5, 0xf35e, 0xf307, 0xf2ae, 0xf254, 0xf1f9, 0xf19d, 0xf140, + 0xf0e2, 0xf083, 0xf022, 0xefc1, 0xef5e, 0xeefa, 0xee96, 0xee30, + 0xedc9, 0xed61, 0xecf8, 0xec8e, 0xec23, 0xebb7, 0xeb4a, 0xeadc, + 0xea6d, 0xe9fc, 0xe98b, 0xe919, 0xe8a6, 0xe831, 0xe7bc, 0xe746, + 0xe6cf, 0xe656, 0xe5dd, 0xe563, 0xe4e8, 0xe46b, 0xe3ee, 0xe370, + 0xe2f1, 0xe271, 0xe1f0, 0xe16e, 0xe0eb, 0xe068, 0xdfe3, 0xdf5d, + 0xded7, 0xde4f, 0xddc7, 0xdd3d, 0xdcb3, 0xdc28, 0xdb9c, 0xdb0f, + 0xda82, 0xd9f3, 0xd964, 0xd8d3, 0xd842, 0xd7b0, 0xd71d, 0xd689, + 0xd5f5, 0xd55f, 0xd4c9, 0xd432, 0xd39a, 0xd302, 0xd268, 0xd1ce, + 0xd133, 0xd097, 0xcffb, 0xcf5d, 0xcebf, 0xce20, 0xcd81, 0xcce0, + 0xcc3f, 0xcb9d, 0xcafb, 0xca57, 0xc9b3, 0xc90f, 0xc869, 0xc7c3, + 0xc71c, 0xc675, 0xc5cc, 0xc524, 0xc47a, 0xc3d0, 0xc325, 0xc279, + 0xc1cd, 0xc121, 0xc073, 0xbfc5, 0xbf17, 0xbe67, 0xbdb7, 0xbd07, + 0xbc56, 0xbba4, 0xbaf2, 0xba3f, 0xb98c, 0xb8d8, 0xb824, 0xb76f, + 0xb6b9, 0xb603, 0xb54d, 0xb496, 0xb3de, 0xb326, 0xb26e, 0xb1b5, + 0xb0fb, 0xb041, 0xaf87, 0xaecc, 0xae10, 0xad55, 0xac98, 0xabdc, + 0xab1f, 0xaa61, 0xa9a3, 0xa8e5, 0xa826, 0xa767, 0xa6a7, 0xa5e8, + 0xa527, 0xa467, 0xa3a6, 0xa2e5, 0xa223, 0xa161, 0xa09f, 0x9fdc, + 0x9f19, 0x9e56, 0x9d93, 0x9ccf, 0x9c0b, 0x9b47, 0x9a82, 0x99bd, + 0x98f8, 0x9833, 0x976d, 0x96a7, 0x95e1, 0x951b, 0x9455, 0x938e, + 0x92c7, 0x9201, 0x9139, 0x9072, 0x8fab, 0x8ee3, 0x8e1b, 0x8d53, + 0x8c8b, 0x8bc3, 0x8afb, 0x8a32, 0x896a, 0x88a1, 0x87d9, 0x8710, + 0x8647, 0x857e, 0x84b6, 0x83ed, 0x8324, 0x825b, 0x8192, 0x80c9, + 0x8000, 0x7f36, 0x7e6d, 0x7da4, 0x7cdb, 0x7c12, 0x7b49, 0x7a81, + 0x79b8, 0x78ef, 0x7826, 0x775e, 0x7695, 0x75cd, 0x7504, 0x743c, + 0x7374, 0x72ac, 0x71e4, 0x711c, 0x7054, 0x6f8d, 0x6ec6, 0x6dfe, + 0x6d38, 0x6c71, 0x6baa, 0x6ae4, 0x6a1e, 0x6958, 0x6892, 0x67cc, + 0x6707, 0x6642, 0x657d, 0x64b8, 0x63f4, 0x6330, 0x626c, 0x61a9, + 0x60e6, 0x6023, 0x5f60, 0x5e9e, 0x5ddc, 0x5d1a, 0x5c59, 0x5b98, + 0x5ad8, 0x5a17, 0x5958, 0x5898, 0x57d9, 0x571a, 0x565c, 0x559e, + 0x54e0, 0x5423, 0x5367, 0x52aa, 0x51ef, 0x5133, 0x5078, 0x4fbe, + 0x4f04, 0x4e4a, 0x4d91, 0x4cd9, 0x4c21, 0x4b69, 0x4ab2, 0x49fc, + 0x4946, 0x4890, 0x47db, 0x4727, 0x4673, 0x45c0, 0x450d, 0x445b, + 0x43a9, 0x42f8, 0x4248, 0x4198, 0x40e8, 0x403a, 0x3f8c, 0x3ede, + 0x3e32, 0x3d86, 0x3cda, 0x3c2f, 0x3b85, 0x3adb, 0x3a33, 0x398a, + 0x38e3, 0x383c, 0x3796, 0x36f0, 0x364c, 0x35a8, 0x3504, 0x3462, + 0x33c0, 0x331f, 0x327e, 0x31df, 0x3140, 0x30a2, 0x3004, 0x2f68, + 0x2ecc, 0x2e31, 0x2d97, 0x2cfd, 0x2c65, 0x2bcd, 0x2b36, 0x2aa0, + 0x2a0a, 0x2976, 0x28e2, 0x284f, 0x27bd, 0x272c, 0x269b, 0x260c, + 0x257d, 0x24f0, 0x2463, 0x23d7, 0x234c, 0x22c2, 0x2238, 0x21b0, + 0x2128, 0x20a2, 0x201c, 0x1f97, 0x1f14, 0x1e91, 0x1e0f, 0x1d8e, + 0x1d0e, 0x1c8f, 0x1c11, 0x1b94, 0x1b17, 0x1a9c, 0x1a22, 0x19a9, + 0x1930, 0x18b9, 0x1843, 0x17ce, 0x1759, 0x16e6, 0x1674, 0x1603, + 0x1592, 0x1523, 0x14b5, 0x1448, 0x13dc, 0x1371, 0x1307, 0x129e, + 0x1236, 0x11cf, 0x1169, 0x1105, 0x10a1, 0x103e, 0xfdd, 0xf7c, + 0xf1d, 0xebf, 0xe62, 0xe06, 0xdab, 0xd51, 0xcf8, 0xca1, + 0xc4a, 0xbf5, 0xba0, 0xb4d, 0xafb, 0xaaa, 0xa5a, 0xa0c, + 0x9be, 0x972, 0x927, 0x8dd, 0x894, 0x84c, 0x805, 0x7c0, + 0x77b, 0x738, 0x6f6, 0x6b5, 0x676, 0x637, 0x5fa, 0x5be, + 0x583, 0x549, 0x511, 0x4d9, 0x4a3, 0x46e, 0x43a, 0x407, + 0x3d6, 0x3a6, 0x377, 0x349, 0x31c, 0x2f1, 0x2c6, 0x29d, + 0x276, 0x24f, 0x22a, 0x205, 0x1e2, 0x1c1, 0x1a0, 0x181, + 0x163, 0x146, 0x12a, 0x110, 0xf6, 0xde, 0xc8, 0xb2, + 0x9e, 0x8b, 0x79, 0x68, 0x59, 0x4b, 0x3e, 0x32, + 0x27, 0x1e, 0x16, 0xf, 0xa, 0x6, 0x2, 0x1, + 0x0, 0x1, 0x2, 0x6, 0xa, 0xf, 0x16, 0x1e, + 0x27, 0x32, 0x3e, 0x4b, 0x59, 0x68, 0x79, 0x8b, + 0x9e, 0xb2, 0xc8, 0xde, 0xf6, 0x110, 0x12a, 0x146, + 0x163, 0x181, 0x1a0, 0x1c1, 0x1e2, 0x205, 0x22a, 0x24f, + 0x276, 0x29d, 0x2c6, 0x2f1, 0x31c, 0x349, 0x377, 0x3a6, + 0x3d6, 0x407, 0x43a, 0x46e, 0x4a3, 0x4d9, 0x511, 0x549, + 0x583, 0x5be, 0x5fa, 0x637, 0x676, 0x6b5, 0x6f6, 0x738, + 0x77b, 0x7c0, 0x805, 0x84c, 0x894, 0x8dd, 0x927, 0x972, + 0x9be, 0xa0c, 0xa5a, 0xaaa, 0xafb, 0xb4d, 0xba0, 0xbf5, + 0xc4a, 0xca1, 0xcf8, 0xd51, 0xdab, 0xe06, 0xe62, 0xebf, + 0xf1d, 0xf7c, 0xfdd, 0x103e, 0x10a1, 0x1105, 0x1169, 0x11cf, + 0x1236, 0x129e, 0x1307, 0x1371, 0x13dc, 0x1448, 0x14b5, 0x1523, + 0x1592, 0x1603, 0x1674, 0x16e6, 0x1759, 0x17ce, 0x1843, 0x18b9, + 0x1930, 0x19a9, 0x1a22, 0x1a9c, 0x1b17, 0x1b94, 0x1c11, 0x1c8f, + 0x1d0e, 0x1d8e, 0x1e0f, 0x1e91, 0x1f14, 0x1f97, 0x201c, 0x20a2, + 0x2128, 0x21b0, 0x2238, 0x22c2, 0x234c, 0x23d7, 0x2463, 0x24f0, + 0x257d, 0x260c, 0x269b, 0x272c, 0x27bd, 0x284f, 0x28e2, 0x2976, + 0x2a0a, 0x2aa0, 0x2b36, 0x2bcd, 0x2c65, 0x2cfd, 0x2d97, 0x2e31, + 0x2ecc, 0x2f68, 0x3004, 0x30a2, 0x3140, 0x31df, 0x327e, 0x331f, + 0x33c0, 0x3462, 0x3504, 0x35a8, 0x364c, 0x36f0, 0x3796, 0x383c, + 0x38e3, 0x398a, 0x3a33, 0x3adb, 0x3b85, 0x3c2f, 0x3cda, 0x3d86, + 0x3e32, 0x3ede, 0x3f8c, 0x403a, 0x40e8, 0x4198, 0x4248, 0x42f8, + 0x43a9, 0x445b, 0x450d, 0x45c0, 0x4673, 0x4727, 0x47db, 0x4890, + 0x4946, 0x49fc, 0x4ab2, 0x4b69, 0x4c21, 0x4cd9, 0x4d91, 0x4e4a, + 0x4f04, 0x4fbe, 0x5078, 0x5133, 0x51ef, 0x52aa, 0x5367, 0x5423, + 0x54e0, 0x559e, 0x565c, 0x571a, 0x57d9, 0x5898, 0x5958, 0x5a17, + 0x5ad8, 0x5b98, 0x5c59, 0x5d1a, 0x5ddc, 0x5e9e, 0x5f60, 0x6023, + 0x60e6, 0x61a9, 0x626c, 0x6330, 0x63f4, 0x64b8, 0x657d, 0x6642, + 0x6707, 0x67cc, 0x6892, 0x6958, 0x6a1e, 0x6ae4, 0x6baa, 0x6c71, + 0x6d38, 0x6dfe, 0x6ec6, 0x6f8d, 0x7054, 0x711c, 0x71e4, 0x72ac, + 0x7374, 0x743c, 0x7504, 0x75cd, 0x7695, 0x775e, 0x7826, 0x78ef, + 0x79b8, 0x7a81, 0x7b49, 0x7c12, 0x7cdb, 0x7da4, 0x7e6d, 0x7f36 +}; + +static void vidtv_s302m_compute_nunits(struct vidtv_encoder *e) +{ + /* + * match the amount of video units if we are trying to sync with a video + * encoder + */ + if (e->sync && e->sync->video) { + e->nunits = e->sync->nunits; + return; + } + + /* otherwise return enough samples for the timedelta in a single AU */ + e->nunits = 1; +} + +static void +vidtv_s302m_compute_sample_count_v(struct vidtv_encoder *e) +{ + /* compute sample count for VAU[i] in 'sync' */ + u32 vau_duration_usecs; + u32 sample_duration_usecs; + u32 i; + u32 sample_count; + u32 s; + + vau_duration_usecs = USEC_PER_SEC / e->sync->sampling_rate_hz; + sample_duration_usecs = USEC_PER_SEC / e->sampling_rate_hz; + + for (i = 0; i < e->sync->nunits; ++i) { + sample_count = e->samples_per_unit[i]; + s = DIV_ROUND_UP(vau_duration_usecs, sample_duration_usecs); + e->samples_per_unit[i] = s; + } +} + +static void +vidtv_s302m_compute_sample_count(struct vidtv_encoder *e, + u64 elapsed_time_usecs) +{ + /* compute sample count for 'elapsed_time_usecs' */ + u32 sample_duration_usecs = USEC_PER_SEC / e->sampling_rate_hz; + + e->samples_per_unit[0] = elapsed_time_usecs / sample_duration_usecs; +} + +static void vidtv_s302m_compute_pts(struct vidtv_encoder *e) +{ + u32 count = e->previous_sample_count; + u32 i; + + for (i = 0; i < e->nunits; ++i) { + count += e->samples_per_unit[i]; + + e->pts[i] = count * + CLOCK_UNIT_90KHZ / e->sampling_rate_hz; + } +} + +static void vidtv_s302m_compute_pts_v(struct vidtv_encoder *e) +{ + u32 i; + + /* use the same pts for video */ + for (i = 0; i < e->sync->nunits; ++i) + e->pts[i] = e->sync->pts[i]; +} + +static bool vidtv_s302m_get_c_bit(struct vidtv_encoder *e) +{ + /* + * see EBU Tech 3250 2004 clause 5.2.1: minimal implementation of + * channel status + */ + struct vidtv_s302m_ctx *ctx = e->ctx; + bool start_was_z; + + start_was_z = ctx->last_start_preamble == PREAMBLE_Z_F0 || + ctx->last_start_preamble == PREAMBLE_Z_F1; + + if (!start_was_z) + return false; + + ++ctx->current_c_bit; + + /* set the bit only if it is the first C bit after a Z preamble */ + return !(ctx->current_c_bit % CHANNEL_STATUS_BIT_LEN); +} + +static s16 vidtv_s302m_get_sample(struct vidtv_encoder *e) +{ + s16 sample; + s16 ret = 0; + + /* bug somewhere */ + if (e->src_buf_offset > e->src_buf_sz) { + pr_err_ratelimited("overflow detected: %d > %d, wrapping.\n", + e->src_buf_offset, + e->src_buf_sz); + + e->src_buf_offset = 0; + } + + if (e->src_buf_offset >= e->src_buf_sz) { + /* let the source know we are out of data */ + if (e->last_sample_cb) + e->last_sample_cb(e->sample_count); + + e->src_buf_offset = 0; + } + + sample = *(s16 *)(e->src_buf + e->src_buf_offset); + + e->sample_count++; + e->src_buf_offset += sizeof(s16); + + /* reverse so the LSB comes first when serializing */ + while (sample) { + ret <<= 1; + + if (sample & 1) + ret ^= 1; + + sample >>= 1; + } + + return ret; +} + +static void vidtv_s302m_toggle_subframe(struct vidtv_encoder *e) +{ + struct vidtv_s302m_ctx *ctx = e->ctx; + + ctx->is_subframe_a = !ctx->is_subframe_a; +} + +static bool vidtv_s302m_is_block_start(const struct vidtv_encoder *e) +{ + return e->sample_count % S302M_BLOCK_SZ; +} + +static bool vidtv_s302m_get_f_bit(const struct vidtv_encoder *e) +{ + const struct vidtv_s302m_ctx *ctx = e->ctx; + + return vidtv_s302m_is_block_start(e) && ctx->is_subframe_a; +} + +static u8 vidtv_s302m_get_preamble(struct vidtv_encoder *e) +{ + /* + * some codecs might disregard the preambles (e.g. ffmpeg s302m), but + * we implement them according to the specs anyway, because some other + * codecs might rely on them. + */ + + struct vidtv_s302m_ctx *ctx = e->ctx; + + bool start_was_x = ctx->last_start_preamble == PREAMBLE_X_F0 || + ctx->last_start_preamble == PREAMBLE_X_F1; + + bool start_was_z = ctx->last_start_preamble == PREAMBLE_Z_F0 || + ctx->last_start_preamble == PREAMBLE_Z_F1; + + bool is_block_start = vidtv_s302m_is_block_start(e); + + /* bug somewhere: a block always starts with an A subframe */ + if (!ctx->is_subframe_a && is_block_start) { + pr_err_ratelimited("Block should start on 'A' subframe\n"); + ctx->is_subframe_a = true; + } + + if (!ctx->is_subframe_a) + return (ctx->last_f) ? PREAMBLE_Y_F1 : PREAMBLE_Y_F0; + + if (start_was_x && is_block_start) { + if (ctx->last_f) { + ctx->last_start_preamble = PREAMBLE_Z_F0; + return PREAMBLE_Z_F0; + } + + ctx->last_start_preamble = PREAMBLE_Z_F1; + return PREAMBLE_Z_F1; + } + + if (start_was_z && is_block_start) { + if (ctx->last_f) { + ctx->last_start_preamble = PREAMBLE_X_F0; + return PREAMBLE_X_F0; + } + + ctx->last_start_preamble = PREAMBLE_X_F1; + return PREAMBLE_X_F1; + } + + return PREAMBLE_X_F0; +} + +static u32 vidtv_s302m_write_subframe(struct vidtv_encoder *e, + struct vidtv_s302m_subframe_16 *f) +{ + u32 nbytes = 0; + + nbytes += vidtv_memcpy(e->encoder_buf, + e->encoder_buf_offset, + VIDTV_S302M_BUF_SZ, + f, + sizeof(*f)); + + e->encoder_buf_offset += nbytes; + + return nbytes; +} + +static void vidtv_s302m_write_h(struct vidtv_encoder *e, u32 p_sz) +{ + struct vidtv_smpte_s302m_es h = {0}; + u32 nbytes = 0; + + h.bits_per_sample = S302M_BITS_PER_SAMPLE_16; + h.channel_identification = 0; + h.num_channels = S302M_2CHANNELS; + h.audio_packet_size = p_sz; + + nbytes += vidtv_memcpy(e->encoder_buf, + e->encoder_buf_offset, + e->encoder_buf_sz, + &h, + sizeof(h)); + + e->encoder_buf_offset += nbytes; +} + +static void vidtv_s302m_write_frames(struct vidtv_encoder *e) +{ + u32 nbytes = 0; + u32 nbytes_per_unit = 0; + u32 preamble_bit_num = 0; + u32 au_sz = 0; + struct vidtv_s302m_subframe_16 a = {0}; + struct vidtv_s302m_subframe_16 b = {0}; + u8 preamble_a = 0; /* four bits sent per subframe */ + u8 preamble_b = 0; /* four bits sent per subframe */ + u8 aux = 0; + s16 sample = 0; + bool v = true; + bool u = false; + bool c; + bool f; + + u32 i; + u32 j; + + for (i = 0; i < e->nunits; ++i) { + /* stereo: each sample will generate two subframes */ + au_sz = e->samples_per_unit[i] * + sizeof(struct vidtv_s302m_subframe_16) * + 2; + + vidtv_s302m_write_h(e, au_sz); + + for (j = 0; j < e->samples_per_unit[i]; ++j) { + /* keep this in this order */ + preamble_a = vidtv_s302m_get_preamble(e); + sample = vidtv_s302m_get_sample(e); + c = vidtv_s302m_get_c_bit(e); + f = vidtv_s302m_get_f_bit(e); + + a.preamble = preamble_bit_num % 8 ? + (preamble_a & GENMASK(3, 0)) : + (preamble_a & GENMASK(7, 4)) >> 4; + + a.aux = aux; + a.data_word = sample; + a.v = v; + a.u = u; + a.c = c; + a.f = f; + + vidtv_s302m_toggle_subframe(e); + + preamble_b = vidtv_s302m_get_preamble(e); + c = vidtv_s302m_get_c_bit(e); + f = vidtv_s302m_get_f_bit(e); + + b.preamble = preamble_bit_num % 8 ? + (preamble_b & GENMASK(3, 0)) : + (preamble_b & GENMASK(7, 4)) >> 4; + + b.aux = aux; + b.data_word = sample; + b.v = v; + b.u = u; + b.c = c; + b.f = f; + + preamble_bit_num += 4; + + nbytes_per_unit += vidtv_s302m_write_subframe(e, &a); + nbytes_per_unit += vidtv_s302m_write_subframe(e, &b); + + nbytes += nbytes_per_unit; + + vidtv_s302m_toggle_subframe(e); + } + + e->nbytes[i] = nbytes; + + /* did we write more bytes than we initially computed? */ + if (au_sz != nbytes_per_unit) { + pr_warn_ratelimited("write size was %d, expected %d\n", + au_sz, + nbytes_per_unit); + } + + e->offsets[i] = nbytes_per_unit; + nbytes_per_unit = 0; + } +} + +static void *vidtv_s302m_encode(struct vidtv_encoder *e, u64 elapsed_time) +{ + /* + * According to SMPTE 302M, an audio access unit is specified as those + * AES3 words that are associated with a corresponding video frame. + * Therefore we should write one AAU for every VAU in the corresponding + * video encoder ('sync'), using the same values for PTS as used by the + * video encoder. + * + * I assume that it is also possible to send audio without any + * associated video, as in a radio-like service. If this is the case, + * we are sending only _one_ AAU with enough audio data for + * 'elapsed_time' instead, computing the value for PTS manually. + */ + + vidtv_s302m_compute_nunits(e); + + if (e->sync && e->sync->video) { + vidtv_s302m_compute_sample_count_v(e); + vidtv_s302m_compute_pts_v(e); + } else { + vidtv_s302m_compute_sample_count(e, elapsed_time); + vidtv_s302m_compute_pts(e); + } + + vidtv_s302m_write_frames(e); + + return e->encoder_buf; +} + +static u8 vidtv_s302m_clear(struct vidtv_encoder *e) +{ + u8 ret = e->nunits; + + e->nunits = 0; + memset(e->samples_per_unit, 0, e->access_unit_capacity); + memset(e->nbytes, 0, e->access_unit_capacity); + memset(e->offsets, 0, e->access_unit_capacity); + memset(e->pts, 0, e->access_unit_capacity); + memset(e->dts, 0, e->access_unit_capacity); + + return ret; +} + +struct vidtv_encoder +*vidtv_s302m_encoder_init(struct vidtv_s302m_encoder_init_args args) +{ + struct vidtv_encoder *e = kzalloc(sizeof(*e), GFP_KERNEL); + u32 priv_sz = sizeof(struct vidtv_s302m_ctx); + + if (args.sync) + args.access_unit_capacity = args.sync->access_unit_capacity; + + e->id = S302M; + + e->name = kzalloc(strlen(args.name), GFP_KERNEL); + strncpy(e->name, args.name, strlen(e->name)); + + e->encoder_buf = vzalloc(VIDTV_S302M_BUF_SZ); + e->encoder_buf_sz = VIDTV_S302M_BUF_SZ; + e->encoder_buf_offset = 0; + + e->sample_count = 0; + e->previous_sample_count = 0; + e->nunits = 0; + + e->samples_per_unit = kcalloc(args.access_unit_capacity, + sizeof(u32), + GFP_KERNEL); + + e->pts = kcalloc(args.access_unit_capacity, sizeof(u64), GFP_KERNEL); + e->dts = kcalloc(args.access_unit_capacity, sizeof(u64), GFP_KERNEL); + + e->nbytes = kcalloc(args.access_unit_capacity, + sizeof(u32), + GFP_KERNEL); + + e->offsets = kcalloc(args.access_unit_capacity, + sizeof(u32), + GFP_KERNEL); + + e->src_buf = (args.src_buf) ? args.src_buf : &s302m_sin_lut; + e->src_buf_sz = (args.src_buf) ? args.src_buf_sz : S302M_SIN_LUT_SZ; + e->src_buf_offset = 0; + + e->video = false; + e->ctx = kzalloc(priv_sz, GFP_KERNEL); + + e->encode = vidtv_s302m_encode; + e->clear = vidtv_s302m_clear; + + e->es_pid = args.es_pid; + + e->sync = args.sync; + e->sampling_rate_hz = S302M_SAMPLING_RATE_HZ; + e->access_unit_capacity = args.access_unit_capacity; + + /* we will wrap around 'src' if this is NULL */ + e->last_sample_cb = args.last_sample_cb; + + e->destroy = vidtv_s302m_encoder_destroy; + + if (args.head) { + while (args.head->next) + args.head = args.head->next; + + args.head->next = e; + } + + e->next = NULL; + + return e; +} + +void vidtv_s302m_encoder_destroy(struct vidtv_encoder *e) +{ + if (e->id != S302M) { + pr_err_ratelimited("Encoder type mismatch, skipping.\n"); + return; + } + + kfree(e->name); + kfree(e->encoder_buf); + kfree(e->samples_per_unit); + kfree(e->pts); + kfree(e->dts); + kfree(e->nbytes); + kfree(e->offsets); + kfree(e->ctx); + kfree(e); +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.h b/drivers/media/test-drivers/vidtv/vidtv_s302m.h new file mode 100644 index 0000000000000..d0ed01cffca27 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the code for an AES3 (also known as AES/EBU) encoder. + * It is based on EBU Tech 3250 and SMPTE 302M technical documents. + * + * This encoder currently supports 16bit AES3 subframes using 16bit signed + * integers. + * + * Note: AU stands for Access Unit, and AAU stands for Audio Access Unit + * + * Written by Daniel W. S. Almeida + */ + +#ifndef VIDTV_S302M_H +#define VIDTV_S302M_H + +#include +#include + +#include "vidtv_encoder.h" + +/* see SMPTE 302M 2007 clause 7.3 */ +#define VIDTV_S302M_BUF_SZ 65024 + +/* see ETSI TS 102 154 v.1.2.1 clause 7.3.5 */ +#define VIDTV_S302M_FORMAT_IDENTIFIER 0x42535344 + +/** + * struct vidtv_s302m_ctx - s302m encoder context. + * @enc: A pointer to the containing encoder structure. + * @last_f: whether the F bit was set for the last sub-frame. + * @last_start_preamble: The start preamble, either X or Z. + * @is_subframe_a: Whether we are writing an A subframe at the moment. + * @current_c_bit: The C bit to send in the current subframe. + */ +struct vidtv_s302m_ctx { + struct vidtv_encoder *enc; + + bool last_f; + u8 last_start_preamble; + bool is_subframe_a; + u8 current_c_bit; +}; + +struct vidtv_smpte_s302m_es { + union { + __be32 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 zero:4; + u16 bits_per_sample:2; /* 0x0 for 16bits */ + u16 channel_identification:8; + u16 num_channels:2; + u16 audio_packet_size:16; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 audio_packet_size:16; + u16 num_channels:2; + u16 channel_identification:8; + u16 bits_per_sample:2; /* 0x0 for 16bits */ + u16 zero:4; +#else +#error "Unknown bit ordering" +#endif + } __packed; + } __packed; +} __packed; + +struct vidtv_s302m_subframe_16 { + union { + u32 bitfield; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 f:1; + u8 c:1; + u8 u:1; + u8 v:1; + u16 data_word:16; /* little endian */ + u8 zero:4; + u8 aux:4; + u8 preamble:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 preamble:4; + u8 aux:4; + u8 zero:4; + u16 data_word:16; /* little endian */ + u8 v:1; + u8 u:1; + u8 c:1; + u8 f:1; +#else +#error "Unknown bit ordering" +#endif + } __packed; + } __packed; +}__packed; + +struct vidtv_s302m_encoder_init_args { + char *name; + void *src_buf; + u32 src_buf_sz; + u16 es_pid; + struct vidtv_encoder *sync; + u8 access_unit_capacity; + void (*last_sample_cb)(u32 sample_no); + + /* optionally chain to this encoder */ + struct vidtv_encoder *head; +}; + +struct vidtv_encoder +*vidtv_s302m_encoder_init(struct vidtv_s302m_encoder_init_args args); + +void vidtv_s302m_encoder_destroy(struct vidtv_encoder *encoder); + +#endif /* VIDTV_S302M_H */