[9610] hdcp: add initial hdcp2
authorTaein, An <taein.an@samsung.com>
Tue, 22 May 2018 23:23:55 +0000 (08:23 +0900)
committerJunho Choi <junhosj.choi@samsung.com>
Thu, 24 May 2018 00:02:11 +0000 (09:02 +0900)
Change-Id: I1af03165b61d864b38e7993db3158f413072ea90
Signed-off-by: Taein, An <taein.an@samsung.com>
37 files changed:
drivers/soc/samsung/Makefile
drivers/soc/samsung/exynos-hdcp/Kconfig [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/Makefile [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-auth.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-auth.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-if.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-if.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-reg.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-config.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-log.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-session.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-session.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-testvector.h [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/hdcp2_if.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-auth.c [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-auth.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-selftest.c [new file with mode: 0644]
drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-selftest.h [new file with mode: 0755]
drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia.c [new file with mode: 0755]

index f2722d59bcb05776d1e05310ac12c0b6c59a26f6..ee2ec4119f43af8bcd42a31c17b5b19b8cabd8c4 100644 (file)
@@ -48,3 +48,6 @@ obj-$(CONFIG_ARCH_EXYNOS)     += exynos-pm.o
 
 # Exynos Secure Log
 obj-$(CONFIG_EXYNOS_SECURE_LOG)        += exynos-seclog.o
+
+# HDCP
+obj-$(CONFIG_EXYNOS_HDCP2)  += exynos-hdcp/
diff --git a/drivers/soc/samsung/exynos-hdcp/Kconfig b/drivers/soc/samsung/exynos-hdcp/Kconfig
new file mode 100755 (executable)
index 0000000..1a35571
--- /dev/null
@@ -0,0 +1,37 @@
+#
+# HDCP driver configuration
+#
+
+menuconfig EXYNOS_HDCP2
+       bool "HDCP2 driver support"
+       default y
+       help
+         Enable HDCP2 support
+
+if EXYNOS_HDCP2
+
+config HDCP2_EMULATION_MODE
+       bool "HDCP2 emulation mode support"
+       default n
+       help
+         Enable HDCP2 emulation mode support
+
+config HDCP2_DP_ENABLE
+         bool "HDCP2 DP protocol support"
+         default y
+         help
+           Enable HDCP2 DP protocol support
+
+config HDCP2_IIA_ENABLE
+         bool "HDCP2 HDCP protocol support"
+         default n
+         help
+           Enable HDCP2 DP protocol support
+
+config HDCP2_FUNC_TEST_MODE
+         bool "HDCP2 DP Default Enabled"
+         default n
+         help
+           Enable HDCP2 DP Function test mode.
+
+endif
diff --git a/drivers/soc/samsung/exynos-hdcp/Makefile b/drivers/soc/samsung/exynos-hdcp/Makefile
new file mode 100644 (file)
index 0000000..090abed
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Exynos HDCP drivers
+#
+
+# HDCP
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2-session.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2-teeif.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2-misc.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2-encrypt.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2-crypto.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += exynos-hdcp2-protocol-msg.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += iia_link/exynos-hdcp2-iia-auth.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += iia_link/exynos-hdcp2-iia.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += iia_link/exynos-hdcp2-iia-selftest.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += dp_link/exynos-hdcp2-dplink.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += dp_link/exynos-hdcp2-dplink-if.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += dp_link/exynos-hdcp2-dplink-auth.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += dp_link/exynos-hdcp2-dplink-protocol-msg.o
+obj-$(CONFIG_EXYNOS_HDCP2)     += dp_link/exynos-hdcp2-dplink-selftest.o
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-auth.c b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-auth.c
new file mode 100644 (file)
index 0000000..59f0fea
--- /dev/null
@@ -0,0 +1,1203 @@
+/* drivers/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-dplink-auth.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#include <linux/delay.h>
+#include "../exynos-hdcp2.h"
+#include "../exynos-hdcp2-misc.h"
+#include "../exynos-hdcp2-log.h"
+#include "exynos-hdcp2-dplink-auth.h"
+#include "exynos-hdcp2-dplink-if.h"
+#include "exynos-hdcp2-dplink.h"
+
+#define MAX_LC_RETRY 10
+
+static uint8_t pairing_ready;
+static uint8_t hprime_ready;
+uint8_t rp_ready;
+uint8_t rp_ready_s;
+static uint8_t reauth_req;
+static uint8_t integrity_fail;
+
+static char *hdcp_msgid_str[] = {
+       NULL,
+       "Null message",
+       "DP_AKE_Init",
+       "DP_AKE_Send_Cert",
+       "DP_AKE_No_Stored_km",
+       "DP_AKE_Stored_km",
+       "DP_AKE_Send_rrx",
+       "DP_AKE_Send_H_prime",
+       "DP_AKE_Send_Pairing_Info",
+       "DP_LC_Init",
+       "DP_LC_Send_L_prime",
+       "DP_SKE_Send_Eks",
+       "DP_RepeaterAuth_Send_ReceiverID_List",
+       "DP_RTT_Ready",
+       "DP_RTT_Challenge",
+       "DP_RepeaterAuth_Send_Ack",
+       "DP_RepeaterAuth_Stream_Manage",
+       "DP_RepeaterAuth_Stream_Ready",
+       "DP_Receiver_AuthStatus",
+       "DP_AKE_Transmitter_Info",
+       "DPAKE_Receiver_Info",
+       NULL
+};
+
+struct dp_ake_init {
+       uint8_t rtx[HDCP_AKE_RTX_BYTE_LEN];
+       uint8_t txcaps[HDCP_CAPS_BYTE_LEN];
+};
+
+struct dp_ake_send_cert {
+       uint8_t cert_rx[HDCP_RX_CERT_LEN];
+       uint8_t rrx[HDCP_RRX_BYTE_LEN];
+       uint8_t rxcaps[HDCP_CAPS_BYTE_LEN];
+};
+
+struct dp_ake_no_stored_km {
+       uint8_t ekpub_km[HDCP_AKE_ENCKEY_BYTE_LEN];
+};
+
+struct dp_ake_stored_km {
+       uint8_t ekh_km[HDCP_AKE_EKH_MKEY_BYTE_LEN];
+       uint8_t m[HDCP_AKE_M_BYTE_LEN];
+};
+
+struct dp_ake_send_h_prime {
+       uint8_t h_prime[HDCP_HMAC_SHA256_LEN];
+};
+
+struct dp_ake_send_pairing_info {
+       uint8_t ekh_km[HDCP_AKE_MKEY_BYTE_LEN];
+};
+
+struct dp_lc_init {
+       uint8_t rn[HDCP_RTX_BYTE_LEN];
+};
+
+struct dp_lc_send_l_prime {
+       uint8_t l_prime[HDCP_HMAC_SHA256_LEN];
+};
+
+struct dp_ske_send_eks {
+       uint8_t edkey_ks[HDCP_AKE_MKEY_BYTE_LEN];
+       uint8_t riv[HDCP_RTX_BYTE_LEN];
+};
+
+struct dp_rp_send_rcvid_list {
+       uint8_t rx_info[HDCP_RP_RX_INFO_LEN];
+       uint8_t seq_num_v[HDCP_RP_SEQ_NUM_V_LEN];
+       uint8_t v_prime[HDCP_RP_HMAC_V_LEN / 2];
+       uint8_t rcvid_list[HDCP_RP_RCVID_LIST_LEN];
+};
+
+struct dp_rp_send_ack {
+       uint8_t v[HDCP_RP_HMAC_V_LEN / 2];
+};
+
+struct dp_rp_stream_manage {
+       uint8_t seq_num_m[HDCP_RP_SEQ_NUM_M_LEN];
+       uint8_t k[HDCP_RP_K_LEN];
+       uint8_t streamid_type[HDCP_RP_MAX_STREAMID_TYPE_LEN];
+};
+
+struct dp_rp_stream_ready {
+       uint8_t m_prime[HDCP_RP_HMAC_M_LEN];
+};
+
+struct rxinfo {
+       uint8_t depth;
+       uint8_t dev_count;
+       uint8_t max_dev_exd;
+       uint8_t max_cascade_exd;
+       uint8_t hdcp20_downstream;
+       uint8_t hdcp1x_downstream;
+};
+
+static uint16_t dp_htons(uint16_t x)
+{
+       return (
+               ((x & 0x00FF) << 8) |
+               ((x & 0xFF00) >> 8)
+       );
+}
+
+static void rxinfo_convert_arr2st(uint8_t *arr, struct rxinfo *st)
+{
+       uint16_t rxinfo_val;
+
+       memcpy((uint8_t *)&rxinfo_val, arr, sizeof(rxinfo_val));
+       /* convert to little endian */
+       rxinfo_val = dp_htons(rxinfo_val);
+
+       st->depth = (rxinfo_val >> DEPTH_SHIFT) & DEPTH_MASK;
+       st->dev_count = (rxinfo_val >> DEV_COUNT_SHIFT) & DEV_COUNT_MASK;
+       st->max_dev_exd = (rxinfo_val >> DEV_EXD_SHIFT) & DEV_EXD_MASK;
+       st->max_cascade_exd = (rxinfo_val >> CASCADE_EXD_SHIFT) & CASCADE_EXD_MASK;
+       st->hdcp20_downstream = (rxinfo_val >> HDCP20_DOWN_SHIFT) & HDCP20_DOWN_MASK;
+       st->hdcp1x_downstream = (rxinfo_val >> HDCP1X_DOWN_SHIFT) & HDCP1X_DOWN_MASK;
+}
+
+#if defined(HDCP_DEBUG)
+static void rxinfo_print_all_info(struct rxinfo *rx)
+{
+       hdcp_info("RxInfo:\n");
+       hdcp_info("rxinfo.depth(%d)\n", rx->depth);
+       hdcp_info("rxinfo.dev_count(%d)\n", rx->dev_count);
+       hdcp_info("rxinfo.dev_exd(%d)\n", rx->max_dev_exd);
+       hdcp_info("rxinfo.cascade_exd(%d)\n", rx->max_cascade_exd);
+       hdcp_info("rxinfo.hdcp20_down(%d)\n", rx->hdcp20_downstream);
+       hdcp_info("rxinfo.hdcp1x_down(%d)\n", rx->hdcp1x_downstream);
+}
+#endif
+static int is_auth_aborted(void)
+{
+       /* todo: need mutex */
+       if (integrity_fail || reauth_req) {
+               /* clear flag */
+               dplink_clear_irqflag_all();
+               hdcp_err("Authentication is aborted\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int dp_send_protocol_msg(struct hdcp_link_data *lk, uint8_t msg_id, struct hdcp_msg_info *msg_info)
+{
+        int ret = TX_AUTH_SUCCESS;
+
+        hdcp_info("Tx->Rx: %s\n", hdcp_msgid_str[msg_id]);
+       ret = cap_protocol_msg(msg_id,
+                       msg_info->msg,
+                       (size_t *)&msg_info->msg_len,
+                       HDCP_LINK_TYPE_DP,
+                       &lk->tx_ctx,
+                       &lk->rx_ctx);
+        if (ret) {
+                hdcp_err("cap_protocol_msg() failed. ret(0x%08x)\n", ret);
+                return -TX_AUTH_ERROR_MAKE_PROTO_MSG;
+        }
+
+        return TX_AUTH_SUCCESS;
+}
+
+int dp_recv_protocol_msg(struct hdcp_link_data *lk, uint8_t msg_id, struct hdcp_msg_info *msg_info)
+{
+       int ret = TX_AUTH_SUCCESS;
+
+       if (msg_info->msg_len > 0) {
+               /* parsing received message */
+               ret = decap_protocol_msg(msg_id,
+                               msg_info->msg,
+                               msg_info->msg_len,
+                               HDCP_LINK_TYPE_DP,
+                               &lk->tx_ctx,
+                               &lk->rx_ctx);
+               if (ret) {
+                       hdcp_err("decap_protocol_msg() failed. msg_id(%d), ret(0x%08x)\n", msg_id, ret);
+                       ret = -TX_AUTH_ERROR_WRONG_MSG;
+               }
+       }
+
+       hdcp_info("Rx->Tx: %s\n", hdcp_msgid_str[msg_id]);
+
+       return ret;
+}
+
+static int dp_ake_find_masterkey(int *found)
+{
+       int ret = TX_AUTH_SUCCESS;
+       uint8_t ekh_mkey[HDCP_AKE_EKH_MKEY_BYTE_LEN] = {0};
+       uint8_t m[HDCP_AKE_M_BYTE_LEN] = {0};
+
+       ret = ake_find_masterkey(found,
+               ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN,
+               m, HDCP_AKE_M_BYTE_LEN);
+       if (ret) {
+               hdcp_err("fail to find stored km: ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_get_hdcp_session_key(struct hdcp_link_data *lk)
+{
+       return 0;
+}
+
+static int dp_put_hdcp_session_key(struct hdcp_link_data *lk)
+{
+       return 0;
+}
+
+static int do_send_ake_init(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ake_init *m_init;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_AKE_INIT, &msg_info);
+       if (ret < 0) {
+               hdcp_err("AKE_Init failed: ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_init = (struct dp_ake_init *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_RTX_W,
+                       m_init->rtx,
+                       sizeof(m_init->rtx));
+       if (ret) {
+               hdcp_err("rtx send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_send(HDCP22_MSG_TXCAPS_W,
+                       m_init->txcaps,
+                       sizeof(m_init->txcaps));
+       if (ret) {
+               hdcp_err("txcaps send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+void parse_rxcaps_info(uint8_t *rxcaps, struct hdcp_link_data *lk)
+{
+       memcpy(lk->rx_ctx.caps, rxcaps, sizeof(lk->rx_ctx.caps));
+       if (rxcaps[2] & DP_RXCAPS_REPEATER) {
+               hdcp_info("Rx is Repeater. rxcaps(0x%02x%02x%02x)\n",
+                       rxcaps[0], rxcaps[1], rxcaps[2]);
+               lk->rx_ctx.repeater = 1;
+       } else {
+               hdcp_info("Rx is NOT Repeater. rxcaps(0x%02x%02x%02x)\n",
+                       rxcaps[0], rxcaps[1], rxcaps[2]);
+               lk->rx_ctx.repeater = 0;
+       }
+}
+
+static int do_recv_ake_send_cert(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ake_send_cert m_send_cert;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_CERT_RX_R,
+                       m_send_cert.cert_rx,
+                       sizeof(m_send_cert.cert_rx));
+       if (ret) {
+               hdcp_err("ake_send_cert cert recv fail. ret(%d)\n", ret);
+               return -1;
+       }
+
+#if defined(HDCP_DEBUG)
+       hdcp_debug("rx cert:\n");
+       hdcp_hexdump(m_send_cert.cert_rx, sizeof(m_send_cert.cert_rx));
+#endif
+       ret = hdcp_dplink_recv(HDCP22_MSG_RRX_R,
+                       m_send_cert.rrx,
+                       sizeof(m_send_cert.rrx));
+       if (ret) {
+               hdcp_err("HDCP : ake_send_cert rrx recv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+#if defined(HDCP_DEBUG)
+       hdcp_debug("rx rrx:\n");
+       hdcp_hexdump(m_send_cert.rrx,  sizeof(m_send_cert.rrx));
+#endif
+       ret = hdcp_dplink_recv(HDCP22_MSG_RXCAPS_R,
+                       m_send_cert.rxcaps,
+                       sizeof(m_send_cert.rxcaps));
+       if (ret) {
+               hdcp_err("ake_send_cert rxcaps recv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       parse_rxcaps_info(m_send_cert.rxcaps, lk);
+
+#if defined(HDCP_DEBUG)
+       hdcp_debug("rx caps\n");
+       hdcp_hexdump(m_send_cert.rxcaps,  sizeof(m_send_cert.rxcaps));
+#endif
+       memcpy(msg_info.msg, &m_send_cert, sizeof(struct dp_ake_send_cert));
+       msg_info.msg_len = sizeof(struct dp_ake_send_cert);
+       ret = dp_recv_protocol_msg(lk, DP_AKE_SEND_CERT, &msg_info);
+       if (ret < 0) {
+               hdcp_err("recv AKE_Send_Cert failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_send_ake_nostored_km(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ake_no_stored_km *m_nostored_km;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_AKE_NO_STORED_KM, &msg_info);
+       if (ret < 0) {
+               hdcp_err("send AKE_No_Stored_km failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_nostored_km = (struct dp_ake_no_stored_km *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_EKPUB_KM_W,
+                       m_nostored_km->ekpub_km,
+                       sizeof(m_nostored_km->ekpub_km));
+       if (ret) {
+               hdcp_err("ake_no_stored_km send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_send_ake_stored_km(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ake_stored_km *m_stored_km;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_AKE_STORED_KM, &msg_info);
+       if (ret < 0) {
+               hdcp_err("send AKE_stored_km failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_stored_km = (struct dp_ake_stored_km *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_EKH_KM_W,
+                       m_stored_km->ekh_km,
+                       sizeof(m_stored_km->ekh_km));
+       if (ret) {
+               hdcp_err("ake_stored_km send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_send(HDCP22_MSG_M_W,
+                       m_stored_km->m,
+                       sizeof(m_stored_km->m));
+       if (ret) {
+               hdcp_err("ake_stored_km send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_recv_ake_send_h_prime(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ake_send_h_prime m_hprime;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_HPRIME_R,
+                       m_hprime.h_prime,
+                       sizeof(m_hprime.h_prime));
+       if (ret) {
+               hdcp_err("ake_send_h_prime recv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+#if defined(HDCP_DEBUG)
+       hdcp_debug("h_prime\n");
+       hdcp_hexdump(m_hprime.h_prime, sizeof(m_hprime.h_prime));
+#endif
+
+       memcpy(msg_info.msg, &m_hprime, sizeof(struct dp_ake_send_h_prime));
+       msg_info.msg_len = sizeof(struct dp_ake_send_h_prime);
+       ret = dp_recv_protocol_msg(lk, DP_AKE_SEND_H_PRIME, &msg_info);
+       if (ret < 0) {
+               hdcp_err("recv AKE_Send_H_Prime failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_recv_ake_send_pairing_info(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ake_send_pairing_info m_pairing;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_EKH_KM_R,
+                       m_pairing.ekh_km,
+                       sizeof(m_pairing.ekh_km));
+       if (ret) {
+               hdcp_err("ake_send_pairing_info recv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       memcpy(msg_info.msg, &m_pairing, sizeof(struct dp_ake_send_pairing_info));
+       msg_info.msg_len = sizeof(struct dp_ake_send_pairing_info);
+       ret = dp_recv_protocol_msg(lk, DP_AKE_SEND_PAIRING_INFO, &msg_info);
+       if (ret < 0) {
+               hdcp_err("recv AKE_Send_Pairing_Info failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_send_lc_init(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_lc_init *m_lc_init;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_LC_INIT, &msg_info);
+       if (ret < 0) {
+               hdcp_err("send LC_init failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_lc_init = (struct dp_lc_init *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_RN_W,
+                       m_lc_init->rn,
+                       sizeof(m_lc_init->rn));
+       if (ret) {
+               hdcp_err("lc_init send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_recv_lc_send_l_prime(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_lc_send_l_prime m_lprime;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_LPRIME_R,
+                       m_lprime.l_prime,
+                       sizeof(m_lprime.l_prime));
+       if (ret) {
+               hdcp_err("lc_send_l_prime recv fail. ret(%d)\n", ret);
+               return -1;
+       }
+
+       memcpy(msg_info.msg, &m_lprime, sizeof(struct dp_lc_send_l_prime));
+       msg_info.msg_len = sizeof(struct dp_lc_send_l_prime);
+       ret = dp_recv_protocol_msg(lk, DP_LC_SEND_L_PRIME, &msg_info);
+       if (ret < 0) {
+               hdcp_err("HDCP recv LC_Send_L_prime failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_send_ske_send_eks(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_ske_send_eks *m_ske;
+       /* todo:
+        * Currently, SST mode only use 0x00 as type value
+        * MST mode, HDCP driver get type value from DP driver
+        */
+       /* uint8_t type = 0x00; */
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_SKE_SEND_EKS, &msg_info);
+       if (ret < 0) {
+               hdcp_err("send SKE_SEND_EKS failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_ske = (struct dp_ske_send_eks *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_EDKEY_KS0_W,
+                       &m_ske->edkey_ks[0],
+                       sizeof(m_ske->edkey_ks)/2);
+       if (ret) {
+               hdcp_err("SKE_send_eks send fail. ret(%d)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_send(HDCP22_MSG_EDKEY_KS1_W,
+                       &m_ske->edkey_ks[8],
+                       sizeof(m_ske->edkey_ks)/2);
+       if (ret) {
+               hdcp_err("SKE_send_eks send fail. ret(%x)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_send(HDCP22_MSG_RIV_W,
+                       m_ske->riv,
+                       sizeof(m_ske->riv));
+       if (ret) {
+               hdcp_err("SKE_send_eks send fail. ret(%x)\n", ret);
+               return -1;
+       }
+
+#if 0 /* todo: configure type */
+       /* HDCP errata defined stream type.
+        * type info is send only for receiver
+        */
+       if (!lk->rx_ctx.repeater) {
+               ret = hdcp_dplink_send(HDCP22_MSG_TYPE_W,
+                               &type,
+                               sizeof(uint8_t));
+               if (ret) {
+                       hdcp_err("HDCP : SKE_send_eks type send fail: %x\n", ret);
+                       return -1;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+static int cal_rcvid_list_size(uint8_t *rxinfo, uint32_t *rcvid_size)
+{
+       struct rxinfo rx;
+       int ret = 0;
+       rxinfo_convert_arr2st(rxinfo, &rx);
+#if defined(HDCP_DEBUG)
+       rxinfo_print_all_info();
+#endif
+
+       /* rx_list check */
+       if (rx.max_dev_exd || rx.max_cascade_exd)
+               return 1;
+
+       *rcvid_size = rx.dev_count * HDCP_RCV_ID_LEN;
+       return ret;
+}
+
+static int do_recv_rcvid_list(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct dp_rp_send_rcvid_list m_rcvid;
+       uint32_t rcvid_size = 0;
+       struct hdcp_msg_info msg_info;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       memset(&m_rcvid, 0x00, sizeof(m_rcvid));
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_RXINFO_R,
+                       m_rcvid.rx_info,
+                       sizeof(m_rcvid.rx_info));
+       if (ret) {
+               hdcp_err("verify_receiver_id_list rx_info rcv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_SEQ_NUM_V_R,
+                       m_rcvid.seq_num_v,
+                       sizeof(m_rcvid.seq_num_v));
+
+       if (ret) {
+               hdcp_err("verify_receiver_id_list seq_num_v rcv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       if ((m_rcvid.seq_num_v[0] || m_rcvid.seq_num_v[1] || m_rcvid.seq_num_v[2]) && rp_ready < 2) {
+               hdcp_err("Initial seq_num_v is non_zero.\n");
+               return -1;
+       }
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_VPRIME_R,
+                       m_rcvid.v_prime,
+                       sizeof(m_rcvid.v_prime));
+       if (ret) {
+               hdcp_err("verify_receiver_id_list seq_num_v rcv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       ret = cal_rcvid_list_size(m_rcvid.rx_info, &rcvid_size);
+       if (ret) {
+               hdcp_err("Cal_rcvid_list Fail ! (%d)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_RECV_ID_LIST_R,
+                       m_rcvid.rcvid_list,
+                       rcvid_size);
+       if (ret) {
+               hdcp_err("verify_receiver_id_list rcvid_list rcv fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       memcpy(msg_info.msg, &m_rcvid, sizeof(struct dp_rp_send_rcvid_list));
+       msg_info.msg_len = sizeof(struct dp_rp_send_rcvid_list);
+       ret = dp_recv_protocol_msg(lk, DP_REPEATERAUTH_SEND_RECEIVERID_LIST, &msg_info);
+       if (ret < 0) {
+               hdcp_err("recv RepeaterAuth_Send_ReceiverID_List failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_send_rp_ack(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_rp_send_ack *m_ack;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_REPEATERAUTH_SEND_AKE, &msg_info);
+       if (ret < 0) {
+               hdcp_err("send RepeaterAuth_Send_Ack failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_ack = (struct dp_rp_send_ack *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_V_W,
+                       m_ack->v,
+                       sizeof(m_ack->v));
+       if (ret) {
+               hdcp_err("V send fail: ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_send_rp_stream_manage(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_rp_stream_manage *m_strm;
+       uint16_t stream_num;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = dp_send_protocol_msg(lk, DP_REPEATERAUTH_STREAM_MANAGE, &msg_info);
+       if (ret < 0) {
+               hdcp_err("send RepeaterAuth_Stream_Manage. ret(%d)\n", ret);
+               return -1;
+       }
+
+       m_strm = (struct dp_rp_stream_manage *)msg_info.msg;
+       ret = hdcp_dplink_send(HDCP22_MSG_SEQ_NUM_M_W,
+                       m_strm->seq_num_m,
+                       sizeof(m_strm->seq_num_m));
+       if (ret) {
+               hdcp_err("seq_num_M send fail. ret(%d)\n", ret);
+               return -1;
+       }
+
+       ret = hdcp_dplink_send(HDCP22_MSG_K_W,
+                       m_strm->k,
+                       sizeof(m_strm->k));
+       if (ret) {
+               hdcp_err("k send fail. ret(%x)\n", ret);
+               return -1;
+       }
+
+       stream_num = lk->tx_ctx.strm.dp.stream_num;
+       ret = hdcp_dplink_send(HDCP22_MSG_STREAMID_TYPE_W,
+                       m_strm->streamid_type,
+                       stream_num * HDCP_RP_STREAMID_TYPE_LEN);
+       if (ret) {
+               hdcp_err("Streamid_Type send fail. ret(%x)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_recv_rp_stream_ready(struct hdcp_link_data *lk)
+{
+       int ret;
+       struct hdcp_msg_info msg_info;
+       struct dp_rp_stream_ready m_strm_ready;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_MPRIME_R,
+                       m_strm_ready.m_prime,
+                       sizeof(m_strm_ready.m_prime));
+       if (ret) {
+               hdcp_err("M' recv fail. ret(%d)\n", ret);
+               return -1;
+       }
+
+       memcpy(msg_info.msg, &m_strm_ready, sizeof(struct dp_rp_stream_ready));
+       msg_info.msg_len = sizeof(struct dp_rp_stream_ready);
+       ret = dp_recv_protocol_msg(lk, DP_REPEATERAUTH_STREAM_READY, &msg_info);
+
+       if (ret < 0) {
+               hdcp_err("HDCP recv RepeaterAuth_Stream_Ready failed. ret(%d)\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int check_h_prime_ready(void)
+{
+       int i = 0;
+       uint8_t status = 0;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       msleep(100);
+
+       /* HDCP spec is 1 sec. but we give margin 100ms */
+       while (i < 10) {
+               /* check abort state firstly,
+                * if session is abored by Rx, Tx stops Authentication process
+                */
+               if (is_auth_aborted())
+                       return -TX_AUTH_ERROR_ABORT;
+
+               /* received from CP_IRQ */
+               if (hprime_ready) {
+                       /* reset flag */
+                       hprime_ready = 0;
+                       return 0;
+               }
+
+               /* check as polling mode */
+               hdcp_dplink_get_rxstatus(&status);
+               if (status & DP_RXSTATUS_HPRIME_AVAILABLE) {
+                       /* reset flag */
+                       hprime_ready = 0;
+                       return 0;
+               }
+
+               msleep(100);
+               i++;
+       }
+
+       hdcp_err("hprime timeout(%dms)\n", (100 * i));
+       return -1;
+}
+
+static int check_pairing_ready(void)
+{
+       int i = 0;
+       uint8_t status = 0;
+
+       /* check abort state firstly,
+        * if session is abored by Rx, Tx stops Authentication process
+        */
+       if (is_auth_aborted())
+               return -TX_AUTH_ERROR_ABORT;
+
+       msleep(200);
+       /* HDCP spec is 200ms. but we give margin 100ms */
+       while (i < 2) {
+               /* check abort state firstly,
+                * if session is abored by Rx, Tx stops Authentication process
+                */
+               if (is_auth_aborted())
+                       return -TX_AUTH_ERROR_ABORT;
+
+               /* received from CP_IRQ */
+               if (pairing_ready) {
+                       /* reset flag */
+                       pairing_ready = 0;
+                       return 0;
+               }
+
+               /* check as polling mode */
+               hdcp_dplink_get_rxstatus(&status);
+               if (status & DP_RXSTATUS_PAIRING_AVAILABLE) {
+                       /* reset flag */
+                       pairing_ready = 0;
+                       return 0;
+               }
+
+               msleep(100);
+               i++;
+       }
+
+       hdcp_err("pairing timeout(%dms)\n", (100 * i));
+       return -1;
+}
+
+static int check_rcvidlist_ready(void)
+{
+       int i = 0;
+       uint8_t status = 0;
+
+       /* HDCP spec is 3 sec */
+       while (i < 30) {
+               /* check abort state firstly,
+                * if session is abored by Rx, Tx stops Authentication process
+                */
+               if (is_auth_aborted())
+                       return -TX_AUTH_ERROR_ABORT;
+
+               /* received from CP_IRQ */
+               if (rp_ready > rp_ready_s) {
+                       /* reset flag */
+                       rp_ready_s = rp_ready;
+                       return 0;
+               }
+
+               /* check as polling mode */
+               hdcp_dplink_get_rxstatus(&status);
+               if (status & DP_RXSTATUS_READY) {
+                       rp_ready++;
+                       return 0;
+               }
+
+               msleep(100);
+               i++;
+       }
+
+
+       hdcp_err("receiver ID list timeout(%dms)\n", (100 * i));
+       return -TX_AUTH_ERROR_TIME_EXCEED;
+}
+
+int dplink_exchange_master_key(struct hdcp_link_data *lk)
+{
+       int rval = TX_AUTH_SUCCESS;
+       int key_found;
+
+       do {
+               /* send Tx -> Rx: AKE_init */
+               if (do_send_ake_init(lk) < 0) {
+                       hdcp_err("send_ake_int fail\n");
+                       rval = -TX_AUTH_ERROR_SEND_PROTO_MSG;
+                       break;
+               }
+
+               /* HDCP spec defined 100ms as min delay after write AKE_Init */
+               msleep(100);
+
+               /* recv Rx->Tx: AKE_Send_Cert message */
+               if (do_recv_ake_send_cert(lk) < 0) {
+                       hdcp_err("recv_ake_send_cert fail\n");
+                       rval = -TX_AUTH_ERROR_RECV_PROTO_MSG;
+                       break;
+               }
+
+               /* send Tx->Rx: AKE_Stored_km or AKE_No_Stored_km message */
+               if (dp_ake_find_masterkey(&key_found) < 0) {
+                       hdcp_err("find master key fail\n");
+                       rval = -TX_AUTH_ERROR_MAKE_PROTO_MSG;
+                       break;
+               }
+               if (!key_found) {
+                       if (do_send_ake_nostored_km(lk) < 0) {
+                               hdcp_err("ake_send_nostored_km fail\n");
+                               rval = -TX_AUTH_ERROR_SEND_PROTO_MSG;
+                               break;
+                       }
+                       lk->stored_km = HDCP_WITHOUT_STORED_KM;
+               } else {
+                       if (do_send_ake_stored_km(lk) < 0) {
+                               hdcp_err("ake_send_stored_km fail\n");
+                               rval = -TX_AUTH_ERROR_SEND_PROTO_MSG;
+                               break;
+                       }
+                       lk->stored_km = HDCP_WITH_STORED_KM;
+               }
+
+               if (check_h_prime_ready() < 0) {
+                       hdcp_err("Cannot read H prime\n");
+                       rval = -TX_AUTH_ERROR_RECV_PROTO_MSG;
+                       break;
+               }
+
+               /* recv Rx->Tx: AKE_Send_H_Prime message */
+               if (do_recv_ake_send_h_prime(lk) < 0) {
+                       hdcp_err("recv_ake_send_h_prime fail\n");
+                       rval = -TX_AUTH_ERROR_RECV_PROTO_MSG;
+                       break;
+               }
+
+               if (lk->stored_km == HDCP_WITHOUT_STORED_KM) {
+                       if (check_pairing_ready() < 0) {
+                               hdcp_err("Cannot read pairing info\n");
+                               rval = -TX_AUTH_ERROR_RECV_PROTO_MSG;
+                               break;
+                       }
+
+                       /* recv Rx->Tx: AKE_Send_Pairing_Info message */
+                       if (do_recv_ake_send_pairing_info(lk) < 0) {
+                               hdcp_err("recv_ake_send_h_prime fail\n");
+                               rval = -TX_AUTH_ERROR_RECV_PROTO_MSG;
+                               break;
+                       }
+               }
+       } while (0);
+
+       return rval;
+}
+
+int dplink_locality_check(struct hdcp_link_data *lk)
+{
+       int i;
+
+       for (i = 0; i < MAX_LC_RETRY; i++) {
+               /* send Tx -> Rx: LC_init */
+               if (do_send_lc_init(lk) < 0) {
+                       hdcp_err("send_lc_init fail\n");
+                       return -TX_AUTH_ERROR_SEND_PROTO_MSG;
+               }
+
+               /* wait until max dealy */
+               msleep(7);
+
+               /* recv Rx -> Tx: LC_Send_L_Prime */
+               if (do_recv_lc_send_l_prime(lk) < 0) {
+                       hdcp_err("recv_lc_send_l_prime fail\n");
+                       /* retry */
+                       continue;
+               } else {
+                       hdcp_debug("LC success. retryed(%d)\n", i);
+                       break;
+               }
+       }
+
+       if (i == MAX_LC_RETRY) {
+               hdcp_err("LC check fail. exceed retry count(%d)\n", i);
+               return -TX_AUTH_ERROR_RETRYCOUNT_EXCEED;
+       }
+
+       return TX_AUTH_SUCCESS;
+}
+
+int dplink_exchange_session_key(struct hdcp_link_data *lk)
+{
+       /* find session key from the session data */
+       if (dp_get_hdcp_session_key(lk) < 0) {
+               hdcp_err("dp_get_hdcp_session_key() failed\n");
+               return -TX_AUTH_ERRRO_RESTORE_SKEY;
+       }
+
+       /* Send Tx -> Rx: SKE_Send_Eks */
+       if (do_send_ske_send_eks(lk) < 0) {
+               hdcp_err("send_ske_send_eks fail\n");
+               return -TX_AUTH_ERROR_SEND_PROTO_MSG;
+       }
+
+       if (dp_put_hdcp_session_key(lk) < 0) {
+               hdcp_err("put_hdcp_session_key() failed\n");
+               return -TX_AUTH_ERROR_STORE_SKEY;
+       }
+
+       return TX_AUTH_SUCCESS;
+}
+
+int dplink_evaluate_repeater(struct hdcp_link_data *lk)
+{
+       if (lk->rx_ctx.repeater)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+int dplink_wait_for_receiver_id_list(struct hdcp_link_data *lk)
+{
+       int ret;
+
+       ret = check_rcvidlist_ready();
+       if (ret < 0)
+               return ret;
+       else
+               return TX_AUTH_SUCCESS;
+}
+
+int dplink_verify_receiver_id_list(struct hdcp_link_data *lk)
+{
+       /* recv Rx->Tx: RepeaterAuth_ReceiverID_List message */
+       if (do_recv_rcvid_list(lk) < 0) {
+               hdcp_err("recv_receiverID_list fail\n");
+               return -TX_AUTH_ERROR_RECV_PROTO_MSG;
+       }
+
+       return TX_AUTH_SUCCESS;
+}
+
+int dplink_send_receiver_id_list_ack(struct hdcp_link_data *lk)
+{
+       /* Send Tx -> Rx: RepeaterAuth_Send_Ack */
+       if (do_send_rp_ack(lk) < 0) {
+               hdcp_err("send_rp_ack fail\n");
+               return -TX_AUTH_ERROR_SEND_PROTO_MSG;
+       }
+
+       return TX_AUTH_SUCCESS;
+}
+
+int dplink_determine_rx_hdcp_cap(struct hdcp_link_data *lk)
+{
+       int ret;
+       uint8_t rxcaps[HDCP_CAPS_BYTE_LEN];
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_RXCAPS_R,
+                       rxcaps,
+                       sizeof(rxcaps));
+       if (ret) {
+               hdcp_err("check rx caps recv fail: ret(%d)\n", ret);
+               return -1;
+       }
+#if defined(HDCP_DEBUG)
+       hdcp_debug("rx caps\n");
+       hdcp_hexdump(rxcaps,  sizeof(rxcaps));
+#endif
+       if (!(rxcaps[2] & DP_RXCAPS_HDCP_CAPABLE)) {
+               hdcp_err("Rx is not support HDCP. rxcaps(0x%02x%02x%02x)\n",
+                       rxcaps[0], rxcaps[1], rxcaps[2]);
+               return -1;
+       }
+
+       if (rxcaps[0] != DP_RXCAPS_HDCP_VERSION_2) {
+               hdcp_err("Rx is not HDCP2.x. rxcaps(0x%02x%02x%02x)\n",
+                       rxcaps[0], rxcaps[1], rxcaps[2]);
+               return -1;
+       }
+
+       return 0;
+}
+
+int dplink_stream_manage(struct hdcp_link_data *lk)
+{
+       /* Send Tx -> Rx: RepeaterAuth_Stream_Manage */
+       if (do_send_rp_stream_manage(lk) < 0) {
+               hdcp_err("send_rp_stream_manage fail\n");
+               return -TX_AUTH_ERROR_SEND_PROTO_MSG;
+       }
+
+       /* HDCP spec define 100ms as min delay. But we give 100ms margin */
+       msleep(200);
+
+       /* recv Rx->Tx: RepeaterAuth_Stream_Ready message */
+       if (do_recv_rp_stream_ready(lk) < 0) {
+               hdcp_err("recv_rp_stream_ready fail\n");
+               return -TX_AUTH_ERROR_RECV_PROTO_MSG;
+       }
+
+       return TX_AUTH_SUCCESS;
+}
+
+int dplink_set_paring_available(void)
+{
+       pairing_ready = 1;
+
+       return 0;
+}
+
+int dplink_set_hprime_available(void)
+{
+       hprime_ready = 1;
+
+       return 0;
+}
+
+int dplink_set_rp_ready(void)
+{
+       rp_ready++;
+       return 0;
+}
+
+int dplink_set_reauth_req(void)
+{
+       reauth_req = 1;
+
+       return 0;
+}
+
+int dplink_set_integrity_fail(void)
+{
+       dplink_clear_irqflag_all();
+       integrity_fail = 1;
+
+       return 0;
+}
+
+void dplink_clear_irqflag_all(void)
+{
+       pairing_ready = 0;
+       hprime_ready = 0;
+       rp_ready = 0;
+       rp_ready_s = 0;
+       reauth_req = 0;
+       integrity_fail = 0;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-auth.h b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-auth.h
new file mode 100644 (file)
index 0000000..b7733a9
--- /dev/null
@@ -0,0 +1,32 @@
+/* drivers/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-dplink-auth.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_DPLINK_AUTH_H__
+#define __EXYNOS_HDCP2_DPLINK_AUTH_H__
+
+#include "exynos-hdcp2-dplink-protocol-msg.h"
+
+int dplink_determine_rx_hdcp_cap(struct hdcp_link_data *lk);
+int dplink_exchange_master_key(struct hdcp_link_data *lk);
+int dplink_locality_check(struct hdcp_link_data *lk);
+int dplink_exchange_session_key(struct hdcp_link_data *lk);
+int dplink_evaluate_repeater(struct hdcp_link_data *lk);
+int dplink_wait_for_receiver_id_list(struct hdcp_link_data *lk);
+int dplink_verify_receiver_id_list(struct hdcp_link_data *lk);
+int dplink_send_receiver_id_list_ack(struct hdcp_link_data *lk);
+int dplink_stream_manage(struct hdcp_link_data *lk);
+int dplink_get_rxstatus(uint8_t *status);
+int dplink_set_paring_available(void);
+int dplink_set_hprime_available(void);
+int dplink_set_rp_ready(void);
+int dplink_set_reauth_req(void);
+int dplink_set_integrity_fail(void);
+void dplink_clear_irqflag_all(void);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-if.c b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-if.c
new file mode 100644 (file)
index 0000000..2a285e3
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * drivers/soc/samsung/exynos_hdcp/dp_link/exynos-hdcp2-dplink-if.c
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/sock.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include "../exynos-hdcp2-log.h"
+#include "exynos-hdcp2-dplink-reg.h"
+#include "exynos-hdcp2-dplink-if.h"
+
+#if defined(CONFIG_HDCP2_EMULATION_MODE)
+#define NETLINK_HDCP 31
+#define SOCK_BUF_SIZE (1024 * 512)
+#define NETLINK_PORT 1000
+
+struct sock *nl_sk = NULL;
+struct sk_buff sk_buf;
+uint8_t dplink_wait;
+uint8_t *nl_data;
+struct nlmsghdr *nlh;
+
+void cb_hdcp_nl_recv_msg(struct sk_buff *skb);
+
+int hdcp_dplink_init(void)
+{
+       struct netlink_kernel_cfg cfg = {
+               .input  = cb_hdcp_nl_recv_msg,
+       };
+
+       nl_sk = netlink_kernel_create(&init_net, NETLINK_HDCP, &cfg);
+       if (!nl_sk) {
+               hdcp_err("Error creating socket.\n");
+               return -EINVAL;
+       }
+
+       nl_data = (uint8_t *)kzalloc(SOCK_BUF_SIZE, GFP_KERNEL);
+       if (!nl_data) {
+               hdcp_err("Netlink Socket buffer alloc fail\n");
+               return -EINVAL;
+       }
+
+       dplink_wait = 1;
+
+       return 0;
+}
+
+/* callback for netlink driver */
+void cb_hdcp_nl_recv_msg(struct sk_buff *skb)
+{
+       nlh = (struct nlmsghdr *)skb->data;
+
+       memcpy(nl_data, (uint8_t *)nlmsg_data(nlh), nlmsg_len(nlh));
+       dplink_wait = 0;
+}
+
+static void nl_recv_msg(uint8_t *data, uint32_t size)
+{
+       /* todo: change to not a busy wait */
+       while (dplink_wait) {
+               hdcp_err("wait dplink_wait\n");
+               msleep(1000);
+       }
+
+       memcpy(data, nl_data, size);
+
+       dplink_wait = 1;
+}
+
+int hdcp_dplink_recv(uint32_t msg_name, uint8_t *data, uint32_t size)
+{
+       nl_recv_msg(data, size);
+       return 0;
+}
+
+int hdcp_dplink_send(uint32_t msg_name, uint8_t *data, uint32_t size)
+{
+       struct sk_buff *skb_out;
+       int ret;
+
+       skb_out = nlmsg_new(size, 0);
+       if (!skb_out) {
+               hdcp_err("Failed to allocate new skb\n");
+               return -1;
+       }
+       nlh = nlmsg_put(skb_out, 0, 0, 2, size, NLM_F_REQUEST);
+       if (!nlh) {
+               hdcp_err("fail to nlmsg_put()\n");
+               return -1;
+       }
+
+       NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
+       memcpy(nlmsg_data(nlh), data, size);
+
+       ret = nlmsg_unicast(nl_sk, skb_out, NETLINK_PORT);
+       if (ret < 0) {
+               hdcp_err("Error while sending bak to user\n");
+               return -1;
+       }
+       
+       return 0;
+}
+
+int hdcp_dplink_is_enabled_hdcp22(void)
+{
+       /* todo: check hdcp22 enable */
+       return 1;
+}
+
+void hdcp_dplink_config(int en)
+{
+       /* dummy function */
+}
+#else
+
+
+/* Address define for HDCP within DPCD address space */
+uint32_t dpcd_addr[NUM_HDCP22_MSG_NAME] = {
+       DPCD_ADDR_HDCP22_Rtx,
+       DPCD_ADDR_HDCP22_TxCaps,
+       DPCD_ADDR_HDCP22_cert_rx,
+       DPCD_ADDR_HDCP22_Rrx,
+       DPCD_ADDR_HDCP22_RxCaps,
+       DPCD_ADDR_HDCP22_Ekpub_km,
+       DPCD_ADDR_HDCP22_Ekh_km_w,
+       DPCD_ADDR_HDCP22_m,
+       DPCD_ADDR_HDCP22_Hprime,
+       DPCD_ADDR_HDCP22_Ekh_km_r,
+       DPCD_ADDR_HDCP22_rn,
+       DPCD_ADDR_HDCP22_Lprime,
+       DPCD_ADDR_HDCP22_Edkey0_ks,
+       DPCD_ADDR_HDCP22_Edkey1_ks,
+       DPCD_ADDR_HDCP22_riv,
+       DPCD_ADDR_HDCP22_RxInfo,
+       DPCD_ADDR_HDCP22_seq_num_V,
+       DPCD_ADDR_HDCP22_Vprime,
+       DPCD_ADDR_HDCP22_Rec_ID_list,
+       DPCD_ADDR_HDCP22_V,
+       DPCD_ADDR_HDCP22_seq_num_M,
+       DPCD_ADDR_HDCP22_k,
+       DPCD_ADDR_HDCP22_stream_IDtype,
+       DPCD_ADDR_HDCP22_Mprime,
+       DPCD_ADDR_HDCP22_RxStatus,
+       DPCD_ADDR_HDCP22_Type,
+};
+
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+extern int displayport_dpcd_read_for_hdcp22(u32 address, u32 length, u8 *data);
+extern int displayport_dpcd_write_for_hdcp22(u32 address, u32 length, u8 *data);
+extern void displayport_hdcp22_enable(u32 en);
+
+#else
+int displayport_dpcd_read_for_hdcp22(u32 address, u32 length, u8 *data)
+{
+       return 0;
+}
+int displayport_dpcd_write_for_hdcp22(u32 address, u32 length, u8 *data)
+{
+       return 0;
+}
+void displayport_hdcp22_enable(u32 en)
+{
+       return;
+}
+#endif
+
+int hdcp_dplink_init(void)
+{
+       /* todo */
+       return 0;
+}
+
+void hdcp_dplink_config(int en)
+{
+       displayport_hdcp22_enable(en);
+}
+
+int hdcp_dplink_is_enabled_hdcp22(void)
+{
+       /* todo: check hdcp22 enable */
+       return 1;
+}
+
+/* todo: get stream info from DP */
+#define HDCP_DP_STREAM_NUM     0x01
+static uint8_t stream_id[1] = {0x00};
+int hdcp_dplink_get_stream_info(uint16_t *num, uint8_t *strm_id)
+{
+       *num = HDCP_DP_STREAM_NUM;
+       memcpy(strm_id, stream_id, sizeof(uint8_t) * (*num));
+
+       return 0;
+}
+
+int hdcp_dplink_recv(uint32_t msg_name, uint8_t *data, uint32_t size)
+{
+       int i;
+       int ret;
+       int remain;
+
+       if (size > DPCD_PACKET_SIZE) {
+               for (i = 0; i < (size / DPCD_PACKET_SIZE); i++) {
+                       ret = displayport_dpcd_read_for_hdcp22(
+                               dpcd_addr[msg_name] + i * DPCD_PACKET_SIZE,
+                               DPCD_PACKET_SIZE,
+                               &data[i * DPCD_PACKET_SIZE]);
+                       if (ret) {
+                               hdcp_err("dpcd read fail. ret(%d)\n", ret);
+                               return ret;
+                       }
+               }
+
+               remain = size % DPCD_PACKET_SIZE;
+               if (remain) {
+                       ret = displayport_dpcd_read_for_hdcp22(
+                               dpcd_addr[msg_name] + i * DPCD_PACKET_SIZE,
+                               remain,
+                               &data[i * DPCD_PACKET_SIZE]);
+                       if (ret) {
+                               hdcp_err("dpcd read fail. ret(%d)\n", ret);
+                               return ret;
+                       }
+               }
+               return 0;
+       } else
+               return displayport_dpcd_read_for_hdcp22(dpcd_addr[msg_name], size, data);
+}
+
+int hdcp_dplink_send(uint32_t msg_name, uint8_t *data, uint32_t size)
+{
+       int i;
+       int ret;
+
+       if (size > DPCD_PACKET_SIZE) {
+               for (i = 0; i < (size / DPCD_PACKET_SIZE); i++) {
+                       ret = displayport_dpcd_write_for_hdcp22(
+                               dpcd_addr[msg_name] + i * DPCD_PACKET_SIZE,
+                               DPCD_PACKET_SIZE,
+                               &data[i * DPCD_PACKET_SIZE]);
+                       if (ret) {
+                               hdcp_err("dpcd write fail. ret(%d)\n", ret);
+                               return ret;
+                       }
+               }
+               return 0;
+       }
+       else
+               return displayport_dpcd_write_for_hdcp22(dpcd_addr[msg_name], size, data);
+}
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-if.h b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-if.h
new file mode 100644 (file)
index 0000000..90e0e92
--- /dev/null
@@ -0,0 +1,53 @@
+/* driver/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-dplink-if.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_DPLINK_IF_H__
+#define __EXYNOS_HDCP2_DPLINK_IF_H__
+
+enum hdcp22_msg_name {
+       HDCP22_MSG_RTX_W,
+       HDCP22_MSG_TXCAPS_W,
+       HDCP22_MSG_CERT_RX_R,
+       HDCP22_MSG_RRX_R,
+       HDCP22_MSG_RXCAPS_R,
+       HDCP22_MSG_EKPUB_KM_W,
+       HDCP22_MSG_EKH_KM_W,
+       HDCP22_MSG_M_W,
+       HDCP22_MSG_HPRIME_R,
+       HDCP22_MSG_EKH_KM_R,
+       HDCP22_MSG_RN_W,
+       HDCP22_MSG_LPRIME_R,
+       HDCP22_MSG_EDKEY_KS0_W,
+       HDCP22_MSG_EDKEY_KS1_W,
+       HDCP22_MSG_RIV_W,
+       HDCP22_MSG_RXINFO_R,
+       HDCP22_MSG_SEQ_NUM_V_R,
+       HDCP22_MSG_VPRIME_R,
+       HDCP22_MSG_RECV_ID_LIST_R,
+       HDCP22_MSG_V_W,
+       HDCP22_MSG_SEQ_NUM_M_W,
+       HDCP22_MSG_K_W,
+       HDCP22_MSG_STREAMID_TYPE_W,
+       HDCP22_MSG_MPRIME_R,
+       HDCP22_MSG_RXSTATUS_R,
+       HDCP22_MSG_TYPE_W,
+       NUM_HDCP22_MSG_NAME,
+};
+
+#define DP_HDCP22_ENABLE       1
+#define DP_HDCP22_DISABLE      0
+
+int hdcp_dplink_init(void);
+void hdcp_dplink_config(int en);
+int hdcp_dplink_is_enabled_hdcp22(void);
+int hdcp_dplink_recv(uint32_t msg_name, uint8_t *data, uint32_t size);
+int hdcp_dplink_send(uint32_t msg_name, uint8_t *data, uint32_t size);
+int hdcp_dplink_get_stream_info(uint16_t *num, uint8_t *strm_id);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.c b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.c
new file mode 100644 (file)
index 0000000..ddb865c
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+
+#include "../exynos-hdcp2-config.h"
+#include "../exynos-hdcp2-protocol-msg.h"
+#include "../exynos-hdcp2.h"
+#include "../exynos-hdcp2-misc.h"
+#include "../exynos-hdcp2-log.h"
+#include "exynos-hdcp2-dplink-protocol-msg.h"
+#include "exynos-hdcp2-dplink-if.h"
+
+/* HDCP protocol message capulate & decapulate functions for DP */
+static int dp_cap_ake_init(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_cap_ake_no_stored_km(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_cap_ake_stored_km(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_decap_ake_send_cert(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_decap_ake_send_h_prime(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_decap_ake_send_pairing_info(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_cap_lc_init(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_decap_lc_send_l_prime(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_cap_ske_send_eks(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_decap_rpauth_send_recvid_list(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_cap_rpauth_send_ack(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_cap_rpauth_stream_manage(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int dp_decap_rpauth_stream_ready(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+int (*proto_dp[])(uint8_t *, size_t *,
+                       struct hdcp_tx_ctx *,
+                       struct hdcp_rx_ctx *) = {
+       dp_cap_ake_init,
+       dp_decap_ake_send_cert,
+       dp_cap_ake_no_stored_km,
+       dp_cap_ake_stored_km,
+       NULL, /* not defined in HDCP2.2 DP spec */
+       dp_decap_ake_send_h_prime,
+       dp_decap_ake_send_pairing_info,
+       dp_cap_lc_init,
+       dp_decap_lc_send_l_prime,
+       dp_cap_ske_send_eks,
+       dp_decap_rpauth_send_recvid_list,
+       NULL, /* not defined in HDCP2.2 DP spec */
+       NULL, /* not defined in HDCP2.2 DP spec */
+       dp_cap_rpauth_send_ack,
+       dp_cap_rpauth_stream_manage,
+       dp_decap_rpauth_stream_ready,
+       NULL, /* to do */
+       NULL, /* not defined in HDCP2.2 DP spec */
+       NULL, /* not defined in HDCP2.2 DP spec */
+};
+
+static int dp_rp_verify_stream_ready(uint8_t *m_prime)
+{
+       int ret;
+
+       ret = teei_verify_m_prime(m_prime, NULL, 0);
+       if (ret) {
+               hdcp_err("teei_verify_m_prime() is failed with %x\n", ret);
+               return ERR_RP_INVALID_M_PRIME;
+       }
+
+       return 0;
+}
+
+static int dp_rp_gen_stream_manage(struct dp_stream_info *strm)
+{
+       int ret;
+
+       ret = teei_gen_stream_manage(strm->stream_num,
+                               strm->streamid,
+                               strm->seq_num_m,
+                               strm->k,
+                               strm->streamid_type);
+       if (ret) {
+               hdcp_err("teei_gen_stream_manage() is failed with %x\n", ret);
+               return ERR_RP_GEN_STREAM_MG;
+       }
+
+       return 0;
+}
+
+static int dp_rp_set_rcvid_list(struct hdcp_rpauth_info *rp)
+{
+       int ret;
+
+       ret = teei_set_rcvlist_info(rp->rx_info,
+                               rp->seq_num_v,
+                               rp->v_prime,
+                               rp->u_rcvid.lst,
+                               rp->v,
+                               &rp->valid);
+       if (ret) {
+               hdcp_err("teei_set_rcvid_list() is failed with %x\n", ret);
+               return ERR_RP_VERIFY_RCVLIST;
+       }
+
+       return 0;
+}
+
+static int dp_ake_generate_rtx(uint8_t *rtx, size_t rtx_len,
+                               uint8_t *caps, uint32_t caps_len)
+{
+       int ret;
+
+       ret = teei_gen_rtx(HDCP_LINK_TYPE_DP, rtx, rtx_len, caps, caps_len);
+       if (ret) {
+               hdcp_err("generate_none_secret_key() is failed with %x\n", ret);
+               return ERR_GENERATE_NON_SECKEY;
+       }
+
+       return 0;
+}
+
+static int dp_cap_ake_init(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       size_t rtx_len;
+       uint32_t caps_len;
+       int ret;
+
+       /* Buffer Check */
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       rtx_len = HDCP_AKE_RTX_BYTE_LEN;
+       caps_len = HDCP_CAPS_BYTE_LEN;
+
+       /* Generate rtx */
+       ret = dp_ake_generate_rtx(tx_ctx->rtx, rtx_len,
+                               tx_ctx->caps, caps_len);
+       if (ret) {
+               hdcp_err("failed to generate rtx. ret(%d)\n", ret);
+               return ERR_GENERATE_RTX;
+       }
+
+       /* Make Message */
+       memcpy(&m[0], tx_ctx->rtx, rtx_len);
+       memcpy(&m[rtx_len], tx_ctx->caps, HDCP_CAPS_BYTE_LEN);
+       *m_len = rtx_len + HDCP_CAPS_BYTE_LEN;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("cap_ake_init(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int dp_cap_ake_no_stored_km(uint8_t *m, size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t enc_mkey[HDCP_AKE_ENCKEY_BYTE_LEN];
+       int ret;
+
+       if ((m == NULL) || (m_len == NULL) ||
+               (tx_ctx == NULL) || (rx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Generate/Encrypt master key */
+       ret = ake_generate_masterkey(HDCP_LINK_TYPE_DP,
+                                       enc_mkey,
+                                       sizeof(enc_mkey));
+       /* Generate/Encrypt master key */
+       if (ret)
+               return ret;
+
+       /* Make Message */
+       memcpy(m, enc_mkey, HDCP_AKE_ENCKEY_BYTE_LEN);
+       *m_len = HDCP_AKE_ENCKEY_BYTE_LEN;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("cap_ake_no_stored_km(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int dp_cap_ake_stored_km(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int found_km;
+       int ret;
+
+       if ((m == NULL) || (m_len == NULL) ||
+               (tx_ctx == NULL) || (rx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       ret = ake_find_masterkey(&found_km,
+                               tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN,
+                               tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
+       if (ret || !found_km) {
+               hdcp_err("find_masterkey() is failed with ret(0x%x). found(%d)\n", ret, found_km);
+               return -1;
+       }
+
+       /* Make Message */
+       memcpy(m, tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN);
+       memcpy(&m[HDCP_AKE_EKH_MKEY_BYTE_LEN], tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
+       *m_len = HDCP_AKE_EKH_MKEY_BYTE_LEN + HDCP_AKE_M_BYTE_LEN;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("cap_ake_stored_km(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int dp_decap_ake_send_cert(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = dp_check_received_msg(m, *m_len, HDCP_RX_CERT_LEN +
+                                              HDCP_RRX_BYTE_LEN +
+                                              HDCP_CAPS_BYTE_LEN);
+       if (ret)
+               return ret;
+
+       ret = ake_verify_cert(m, HDCP_RX_CERT_LEN,
+                               &m[HDCP_RX_CERT_LEN], HDCP_RRX_BYTE_LEN,
+                               &m[HDCP_RX_CERT_LEN + HDCP_RRX_BYTE_LEN], HDCP_CAPS_BYTE_LEN);
+       if (ret) {
+               hdcp_err("teei_verify_cert is failed with %x\n", ret);
+               return ERR_VERIFY_CERT;
+       }
+
+       return 0;
+}
+
+static int dp_decap_ake_send_h_prime(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       /* compare H == H' */
+       ret = ake_compare_hmac(m, HDCP_HMAC_SHA256_LEN);
+       if (ret) {
+               hdcp_err("decap_ake_send_h_prime is failed. ret(%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dp_decap_ake_send_pairing_info(uint8_t *m, size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       memcpy(tx_ctx->ekh_mkey, &m[0], HDCP_AKE_EKH_MKEY_BYTE_LEN);
+
+       /* Store the Key */
+       ret = ake_store_master_key(tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN);
+       if (ret) {
+               hdcp_err("failed to store master key. ret(%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int dp_cap_lc_init(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) ||
+           (rx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Generate rn */
+       ret = lc_generate_rn(tx_ctx->rn, HDCP_RTX_BYTE_LEN);
+       if (ret) {
+               hdcp_err("failed to generate rtx. ret(%d)\n", ret);
+               return ret;
+       }
+
+       /* Make Message */
+       memcpy(m, tx_ctx->rn, HDCP_RTX_BYTE_LEN);
+       *m_len = HDCP_RTX_BYTE_LEN;
+
+       return 0;
+}
+
+static int dp_decap_lc_send_l_prime(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t *rx_hmac;
+       size_t len;
+       int ret;
+
+       len = HDCP_HMAC_SHA256_LEN;
+
+       /* No_precomputation: compare hmac
+          precomputation: compare the most significant 128bits of L & L' */
+       rx_hmac = m;
+       ret = lc_compare_hmac(rx_hmac, len);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int dp_cap_ske_send_eks(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       uint8_t enc_skey[HDCP_AKE_MKEY_BYTE_LEN];
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) ||
+           (rx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Generate riv */
+       if (!tx_ctx->share_skey) {
+               ret = ske_generate_riv(tx_ctx->riv);
+               if (ret)
+                       return ERR_GENERATE_RIV;
+       }
+
+       /* Generate encrypted Session Key */
+       ret = ske_generate_sessionkey(HDCP_LINK_TYPE_DP,
+                                       enc_skey,
+                                       tx_ctx->share_skey);
+       if (ret)
+               return ret;
+
+       /* Make Message */
+       memcpy(m, enc_skey, HDCP_AKE_MKEY_BYTE_LEN);
+       memcpy(&m[HDCP_AKE_MKEY_BYTE_LEN], tx_ctx->riv, HDCP_RTX_BYTE_LEN);
+       *m_len = HDCP_AKE_MKEY_BYTE_LEN + HDCP_RTX_BYTE_LEN;
+
+#ifdef HDCP_SKE_DEBUG
+       hdcp_debug("SKE_Send_Eks(%lu)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int dp_decap_rpauth_send_recvid_list(uint8_t *m,
+                                       size_t *m_len,
+                                       struct hdcp_tx_ctx *tx_ctx,
+                                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       struct hdcp_rpauth_info *rp;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("RepeaterAuth_Send_ReceiverID_List(%lu):\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+
+       rp = &tx_ctx->rpauth_info;
+
+       memcpy(rp->rx_info, &m[0], HDCP_RP_RX_INFO_LEN);
+       memcpy(rp->seq_num_v, &m[HDCP_RP_RX_INFO_LEN], HDCP_RP_SEQ_NUM_V_LEN);
+       memcpy(rp->v_prime, &m[HDCP_RP_RX_INFO_LEN + HDCP_RP_SEQ_NUM_V_LEN],
+               HDCP_RP_HMAC_V_LEN / 2);
+       memcpy(rp->u_rcvid.lst,
+               &m[HDCP_RP_RX_INFO_LEN + HDCP_RP_SEQ_NUM_V_LEN + (HDCP_RP_HMAC_V_LEN / 2)],
+               HDCP_RP_RCVID_LIST_LEN);
+
+       /* set receiver id list */
+       ret = dp_rp_set_rcvid_list(rp);
+       if (ret) {
+               hdcp_err("failed to set receiver id list. ret(%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dp_cap_rpauth_send_ack(uint8_t *m,
+                               size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+        /* Make Message */
+       if (tx_ctx->rpauth_info.valid == 0) {
+               memcpy(m, tx_ctx->rpauth_info.v, HDCP_RP_HMAC_V_LEN / 2);
+               *m_len = HDCP_RP_HMAC_V_LEN / 2;
+       } else {
+               *m_len = 0;
+               return ERR_SEND_ACK;
+       }
+
+#ifdef HDCP_TX_REPEATER_DEBUG
+       hdcp_debug("RepeaterAuth_Send_Ack(%u)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+
+       return 0;
+}
+
+static int dp_cap_rpauth_stream_manage(uint8_t *m,
+                               size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       struct dp_stream_info *strm;
+       int ret;
+
+       strm = &tx_ctx->strm.dp;
+
+       hdcp_dplink_get_stream_info(&strm->stream_num, strm->streamid);
+
+       /* set receiver id list */
+       ret = dp_rp_gen_stream_manage(strm);
+       if (ret) {
+               hdcp_err("failed to gen stream manage. ret(%d)\n", ret);
+               return ret;
+       }
+
+       /* Make Message */
+       memcpy(m, strm->seq_num_m, HDCP_RP_SEQ_NUM_M_LEN);
+       memcpy(&m[HDCP_RP_SEQ_NUM_M_LEN], strm->k, HDCP_RP_K_LEN);
+       memcpy(&m[HDCP_RP_SEQ_NUM_M_LEN + HDCP_RP_K_LEN],
+               strm->streamid_type, HDCP_RP_STREAMID_TYPE_LEN * strm->stream_num);
+
+       *m_len = HDCP_RP_SEQ_NUM_M_LEN + \
+               HDCP_RP_K_LEN + \
+               (HDCP_RP_STREAMID_TYPE_LEN * strm->stream_num);
+
+#ifdef HDCP_TX_REPEATER_DEBUG
+        hdcp_debug("RepeaterAuth_Stream_Manage(%u)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int dp_decap_rpauth_stream_ready(uint8_t *m,
+                                       size_t *m_len,
+                                       struct hdcp_tx_ctx *tx_ctx,
+                                       struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t *m_prime;
+       int ret;
+
+       m_prime = m;
+       ret = dp_rp_verify_stream_ready(m_prime);
+       if (ret)
+               return 0;
+
+       return 0;
+}
+
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.h b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-protocol-msg.h
new file mode 100644 (file)
index 0000000..4832677
--- /dev/null
@@ -0,0 +1,65 @@
+/* driver/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-protocol-msg.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_DPLINK_PROTOCOL_MSG_H__
+#define __EXYNOS_HDCP2_DPLINK_PROTOCOL_MSG_H__
+
+#include <linux/types.h>
+
+enum dp_msg_id {
+       DP_AKE_INIT = 2,
+       DP_AKE_SEND_CERT,
+       DP_AKE_NO_STORED_KM,
+       DP_AKE_STORED_KM,
+       DP_AKE_SEND_RRX,
+       DP_AKE_SEND_H_PRIME,
+       DP_AKE_SEND_PAIRING_INFO,
+       DP_LC_INIT,
+       DP_LC_SEND_L_PRIME,
+       DP_SKE_SEND_EKS,
+       DP_REPEATERAUTH_SEND_RECEIVERID_LIST,
+       DP_RTT_READY,
+       DP_RTT_CHALLENGE,
+       DP_REPEATERAUTH_SEND_AKE,
+       DP_REPEATERAUTH_STREAM_MANAGE,
+       DP_REPEATERAUTH_STREAM_READY,
+       DP_RECEIVER_AUTHSTATUS,
+       DP_AKE_TRANSMITTER_INFO,
+       DP_AKE_RECEIVER_INFO,
+};
+
+#define DP_RXSTATUS_READY              (0x1 << 0)
+#define DP_RXSTATUS_HPRIME_AVAILABLE   (0x1 << 1)
+#define DP_RXSTATUS_PAIRING_AVAILABLE  (0x1 << 2)
+#define DP_RXSTATUS_REAUTH_REQ         (0x1 << 3)
+#define DP_RXSTATUS_LINK_INTEGRITY_FAIL        (0x1 << 4)
+
+#define DP_RXCAPS_HDCP_CAPABLE         (0x1 << 1)
+#define DP_RXCAPS_REPEATER             (0x1 << 0)
+#define DP_RXCAPS_HDCP_VERSION_2       (0x2)
+
+/* RxInfo */
+#define DEPTH_SHIFT    (9)
+#define DEPTH_MASK     (0x7)
+#define DEV_COUNT_SHIFT        (4)
+#define DEV_COUNT_MASK (0x1F)
+#define DEV_EXD_SHIFT  (3)
+#define DEV_EXD_MASK   (0x1)
+#define CASCADE_EXD_SHIFT      (2)
+#define CASCADE_EXD_MASK       (0x1)
+#define HDCP20_DOWN_SHIFT      (1)
+#define HDCP20_DOWN_MASK       (0x1)
+#define HDCP1X_DOWN_SHIFT      (0)
+#define HDCP1X_DOWN_MASK       (0x1)
+
+#define dp_check_received_msg(m, m_len, len) \
+       ((m == NULL) ? ERR_WRONG_BUFFER : \
+       ((m_len < len) ? ERR_WRONG_MESSAGE_LENGTH : 0))
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-reg.h b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-reg.h
new file mode 100644 (file)
index 0000000..afd0bca
--- /dev/null
@@ -0,0 +1,42 @@
+/* driver/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-dplink-reg.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_DPLINK_REG_H__
+#define __EXYNOS_HDCP2_DPLINK_REG_H__
+
+#define DPCD_PACKET_SIZE (8)
+
+#define DPCD_ADDR_HDCP22_Rtx           0x69000
+#define DPCD_ADDR_HDCP22_TxCaps                0x69008
+#define DPCD_ADDR_HDCP22_cert_rx       0x6900B
+#define DPCD_ADDR_HDCP22_Rrx           0x69215
+#define DPCD_ADDR_HDCP22_RxCaps                0x6921D
+#define DPCD_ADDR_HDCP22_Ekpub_km      0x69220
+#define DPCD_ADDR_HDCP22_Ekh_km_w      0x692A0
+#define DPCD_ADDR_HDCP22_m             0x692B0
+#define DPCD_ADDR_HDCP22_Hprime                0x692C0
+#define DPCD_ADDR_HDCP22_Ekh_km_r      0x692E0
+#define DPCD_ADDR_HDCP22_rn            0x692F0
+#define DPCD_ADDR_HDCP22_Lprime                0x692F8
+#define DPCD_ADDR_HDCP22_Edkey0_ks     0x69318
+#define DPCD_ADDR_HDCP22_Edkey1_ks     0x69320
+#define DPCD_ADDR_HDCP22_riv           0x69328
+#define DPCD_ADDR_HDCP22_RxInfo                0x69330
+#define DPCD_ADDR_HDCP22_seq_num_V     0x69332
+#define DPCD_ADDR_HDCP22_Vprime                0x69335
+#define DPCD_ADDR_HDCP22_Rec_ID_list   0x69345
+#define DPCD_ADDR_HDCP22_V             0x693E0
+#define DPCD_ADDR_HDCP22_seq_num_M     0x693F0
+#define DPCD_ADDR_HDCP22_k             0x693F3
+#define DPCD_ADDR_HDCP22_stream_IDtype 0x693F5
+#define DPCD_ADDR_HDCP22_Mprime                0x69473
+#define DPCD_ADDR_HDCP22_RxStatus      0x69493
+#define DPCD_ADDR_HDCP22_Type          0x69494
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.c b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.c
new file mode 100644 (file)
index 0000000..159ab7a
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-dplink-selftest.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+
+#include "../exynos-hdcp2-config.h"
+#include "../exynos-hdcp2-protocol-msg.h"
+#include "../exynos-hdcp2-misc.h"
+#include "../exynos-hdcp2-encrypt.h"
+#include "../exynos-hdcp2.h"
+#include "../exynos-hdcp2-log.h"
+#include "exynos-hdcp2-dplink-protocol-msg.h"
+
+#define DP_AKE_INIT_LEN                        11
+#define DP_AKE_NO_STORED_KM_LEN                128
+#define DP_LC_INIT_LEN                 8
+#define DP_SKE_SEND_EKS_LEN            24
+
+/* todo: define DP test vector */
+extern uint8_t msg_rx_lc_send_l_prime_v22[32];
+extern uint8_t msg_rx_send_h_prime_v22[32];
+extern uint8_t msg_rx_send_pairing_info_v22[16];
+extern uint8_t msg_rx_send_cert_v22[533];
+extern unsigned char cert_v22[522];
+extern uint8_t tv_emkey_v22[128];
+extern uint8_t tv_ske_eskey_v22[16];
+extern uint8_t tv_rcvid_list_v22[36];
+extern uint8_t tv_rpauth_v_v22[16];
+extern uint8_t tv_rpauth_stream_manage_v22[7];
+extern uint8_t tv_rpauth_stream_ready_v22[32];
+
+static struct hdcp_tx_ctx g_tx_ctx;
+static struct hdcp_rx_ctx g_rx_ctx;
+#if defined(TEST_VECTOR_1)
+static int dp_utc_rpauth_stream_ready(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(DP_REPEATERAUTH_STREAM_READY,
+                       tv_rpauth_stream_ready_v22,
+                       sizeof(tv_rpauth_stream_ready_v22),
+                       HDCP_LINK_TYPE_DP,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("RPAuth_Stream_Ready is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+static int dp_utc_rpauth_stream_manage(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       uint8_t m[525];
+       size_t m_len;
+       int i;
+
+       ret = cap_protocol_msg(DP_REPEATERAUTH_STREAM_MANAGE,
+                               m,
+                               &m_len,
+                               HDCP_LINK_TYPE_DP,
+                               tx_ctx,
+                               rx_ctx);
+       if (ret) {
+               hdcp_err("RPAuth_Stream_Manage is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* compare V with test vector */
+       for (i = 0; i < m_len; i++)
+               if (tv_rpauth_stream_manage_v22[i] != m[i])
+                       break;
+
+       if (i != m_len) {
+               hdcp_err("stream manage info doesn't match (%dth)\n", i);
+               hdcp_err("stream_manage:\n");
+               hdcp_hexdump(m, m_len);
+               return -1;
+       }
+
+       return 0;
+}
+static int dp_utc_rpauth_send_revid_list(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(DP_REPEATERAUTH_SEND_RECEIVERID_LIST,
+                       tv_rcvid_list_v22,
+                       sizeof(tv_rcvid_list_v22),
+                       HDCP_LINK_TYPE_DP,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("RPAuth_Send_ReceiverID_List is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_utc_rpauth_send_ack(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       uint8_t m[525];
+       size_t m_len;
+       uint32_t v_len;
+       int i;
+
+       v_len = HDCP_RP_HMAC_V_LEN / 2;
+
+       ret = cap_protocol_msg(DP_REPEATERAUTH_SEND_AKE,
+                               m,
+                               &m_len,
+                               HDCP_LINK_TYPE_DP,
+                               tx_ctx,
+                               rx_ctx);
+       if (ret) {
+               hdcp_err("RPAuth_Send_AKE is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* compare V with test vector */
+       for (i = 0; i < v_len; i++)
+               if (tv_rpauth_v_v22[i] != m[i])
+                       break;
+
+       if (i != v_len) {
+               hdcp_err("m doesn't match (%dth)\n", i);
+               return -1;
+       }
+
+       return 0;
+}
+#endif
+static int dp_utc_ske_send_eks(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       tx_ctx->share_skey = 0;
+
+       ret = cap_protocol_msg(SKE_SEND_EKS, m, &m_len, HDCP_LINK_TYPE_DP, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("SKE_Send_Eks is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (m_len != DP_SKE_SEND_EKS_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+       /* compare encrypted session key with test vector */
+       for (i = 0; i < HDCP_AKE_MKEY_BYTE_LEN; i++)
+               if (tv_ske_eskey_v22[i] != m[i])
+                       break;
+
+       if (i != HDCP_AKE_MKEY_BYTE_LEN) {
+               hdcp_err("m doesn't match (%dth)\n", i);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Test SKE APIs */
+static int dp_utc_ske(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx, int *cnt, int *fail)
+{
+       int ret;
+
+       ret = dp_utc_ske_send_eks(tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("utc_ske_send_eks() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+static int dp_utc_lc_send_l_prime(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(LC_SEND_L_PRIME, msg_rx_lc_send_l_prime_v22,
+                       sizeof(msg_rx_send_h_prime_v22),
+                       HDCP_LINK_TYPE_DP,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("LC_Send_L_prime is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_utc_lc_init(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+
+       ret = cap_protocol_msg(LC_INIT, m, &m_len, HDCP_LINK_TYPE_DP, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("LC_Init is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (m_len != DP_LC_INIT_LEN) {
+               hdcp_err("Invalid Message length. len(%zu)\n", m_len);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Test LC APIs */
+static int dp_utc_lc(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx,
+               int *cnt, int *fail)
+{
+       int ret;
+
+       ret = dp_utc_lc_init(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_lc_send_l_prime(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int dp_utc_ake_send_h_prime(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(AKE_SEND_H_PRIME, msg_rx_send_h_prime_v22,
+                       sizeof(msg_rx_send_h_prime_v22),
+                       HDCP_LINK_TYPE_DP,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_Send_H_prime is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_utc_ake_send_pairing_info(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       /* todo: support pairing */
+       int i;
+       int ret;
+       int found_km;
+
+       ret = decap_protocol_msg(AKE_SEND_PAIRING_INFO, msg_rx_send_pairing_info_v22,
+                       sizeof(msg_rx_send_pairing_info_v22),
+                       HDCP_LINK_TYPE_DP,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_Send_Pairing_Info is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       ret = ake_find_masterkey(&found_km,
+                               tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN,
+                               tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
+       if (ret) {
+               hdcp_err("find_masterkey() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (found_km) {
+               for (i = 0; i < HDCP_AKE_MKEY_BYTE_LEN; i++)
+                       if (tx_ctx->ekh_mkey[i] != msg_rx_send_pairing_info_v22[i])
+                               break;
+
+               if (i != HDCP_AKE_MKEY_BYTE_LEN) {
+                       hdcp_err("ekh(m) doesn't match (%dth)\n", i);
+                       return -1;
+               }
+       } else {
+               hdcp_err("ekh(m) is not found\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_utc_ake_no_stored_km(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       memcpy(rx_ctx->cert, cert_v22, HDCP_RX_CERT_LEN);
+
+       ret = cap_protocol_msg(AKE_NO_STORED_KM, m, &m_len, HDCP_LINK_TYPE_DP, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_No_Stored_km is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (m_len != DP_AKE_NO_STORED_KM_LEN) {
+               hdcp_err("Invalid Message length. len(%zu)\n", m_len);
+               return -1;
+       }
+
+       for (i = 0; i < 128; i++)
+               if (m[i] != tv_emkey_v22[i])
+                       break;
+
+       if (i != 128) {
+               hdcp_err("Encryption Master Key ERROR\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_utc_ake_send_cert(struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(AKE_SEND_CERT, msg_rx_send_cert_v22,
+                       sizeof(msg_rx_send_cert_v22),
+                       HDCP_LINK_TYPE_DP,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_Send_Cert is failed with ret, 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int dp_utc_ake_init(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+
+       ret = cap_protocol_msg(DP_AKE_INIT, m, &m_len, HDCP_LINK_TYPE_DP, tx_ctx, NULL);
+       if (ret) {
+               hdcp_err("AKE_Init is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* check message length */
+       if (m_len != DP_AKE_INIT_LEN) {
+               hdcp_err("Invalid Message Length. len(%zu)\n", m_len);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Test AKE APIs */
+static int dp_utc_ake(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx,
+               int *cnt, int *fail)
+{
+       int ret;
+
+       ret = dp_utc_ake_init(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_ake_send_cert(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_ake_no_stored_km(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_ake_send_h_prime(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_ake_send_pairing_info(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* Test RP Auth APIs */
+#if defined(TEST_VECTOR_1)
+static int dp_utc_rpauth(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx,
+               int *cnt, int *fail)
+{
+       int ret;
+
+       ret = dp_utc_rpauth_send_revid_list(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_rpauth_send_ack(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_rpauth_stream_manage(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = dp_utc_rpauth_stream_ready(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+#endif
+
+
+/* Test HDCP API functions */
+int dp_hdcp_protocol_self_test(void)
+{
+       int total_cnt = 0, fail_cnt = 0;
+
+       hdcp_info("[ AKE UTC]\n");
+       if (dp_utc_ake(&g_tx_ctx, &g_rx_ctx, &total_cnt, &fail_cnt) < 0) {
+               hdcp_info("AKE UTC: fail\n");
+               return -1;
+       } else {
+               hdcp_info("AKE UTC: success\n");
+       }
+
+       hdcp_info("\n[ LC UTC]\n");
+       if (dp_utc_lc(&g_tx_ctx, &g_rx_ctx, &total_cnt, &fail_cnt) < 0) {
+               hdcp_info("LC UTC: fail\n");
+               return -1;
+       } else {
+               hdcp_info("LC UTC: success\n");
+       }
+
+       hdcp_info("\n[ SKE UTC]\n");
+       if (dp_utc_ske(&g_tx_ctx, &g_rx_ctx, &total_cnt, &fail_cnt) < 0) {
+               hdcp_info("SKE UTC: fail\n");
+               return -1;
+       } else {
+               hdcp_info("SKE UTC: success\n");
+       }
+
+#if defined(TEST_VECTOR_1)
+       hdcp_info("\n[ RP UTC]\n");
+       if (dp_utc_rpauth(&g_tx_ctx, &g_rx_ctx, &total_cnt, &fail_cnt) < 0) {
+               hdcp_info("RP UTC: fail\n");
+               return -1;
+       } else {
+               hdcp_info("RP UTC: success\n");
+       }
+#endif
+
+       return 0;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.h b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.h
new file mode 100644 (file)
index 0000000..08129e1
--- /dev/null
@@ -0,0 +1,15 @@
+/* todo: include/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink-selftest.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_DPLINK_SELFTEST_H__
+#define __EXYNOS_HDCP2_DPLINK_SELFTEST_H__
+
+int dp_hdcp_protocol_self_test(void);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.c b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.c
new file mode 100644 (file)
index 0000000..ccfab78
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.c
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/smc.h>
+#include <asm/cacheflush.h>
+#include <linux/exynos_ion.h>
+#include <linux/smc.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include "../exynos-hdcp2.h"
+#include "../exynos-hdcp2-log.h"
+#include "exynos-hdcp2-dplink.h"
+#include "exynos-hdcp2-dplink-if.h"
+#include "exynos-hdcp2-dplink-auth.h"
+
+#define HDCP_AUTH_RETRY_COUNT  5
+#define HDCP_AUTH_REAUTH_COUNT 2
+
+#define RECVID_WIAT_RETRY_COUNT        5
+
+#if defined(CONFIG_HDCP2_EMULATION_MODE)
+int dplink_emul_handler(int cmd)
+{
+       /* todo: support hdcp22 emulator */
+       return 0;
+}
+#endif
+
+/* current link data */
+static struct hdcp_link_data *lk_data;
+
+extern struct hdcp_session_list g_hdcp_session_list;
+extern uint8_t rp_ready;
+uint8_t auth_end_flag;
+int drm_flag;
+int dp_link_flag;
+struct hdcp_sess_info ss_info;
+struct hdcp_link_info lk_info;
+
+static char *hdcp_link_st_str[] = {
+       "ST_INIT",
+       "ST_H0_NO_RX_ATTACHED",
+       "ST_H1_TX_LOW_VALUE_CONTENT",
+       "ST_A0_DETERMINE_RX_HDCP_CAP",
+       "ST_A1_EXCHANGE_MASTER_KEY",
+       "ST_A2_LOCALITY_CHECK",
+       "ST_A3_EXCHANGE_SESSION_KEY",
+       "ST_A4_TEST_REPEATER",
+       "ST_A5_AUTHENTICATED",
+       "ST_A6_WAIT_RECEIVER_ID_LIST",
+       "ST_A7_VERIFY_RECEIVER_ID_LIST",
+       "ST_A8_SEND_RECEIVER_ID_LIST_ACK",
+       "ST_A9_CONTENT_STREAM_MGT",
+       "ST_END",
+       NULL
+};
+
+int do_dplink_auth(struct hdcp_link_info *lk_handle)
+{
+       struct hdcp_session_node *ss_node;
+       struct hdcp_link_node *lk_node;
+       int ret = HDCP_SUCCESS;
+       int rval = TX_AUTH_SUCCESS;
+       uint32_t retry_recvid = 0;
+       int hdcp_enabled = 0;
+
+       /* find Session node which contains the Link */
+
+       if (!rp_ready) {
+               ss_node = hdcp_session_list_find(lk_handle->ss_id, &g_hdcp_session_list);
+               if (!ss_node)
+                       return HDCP_ERROR_INVALID_INPUT;
+
+               lk_node = hdcp_link_list_find(lk_handle->lk_id, &ss_node->ss_data->ln);
+               if (!lk_node)
+                       return HDCP_ERROR_INVALID_INPUT;
+
+               lk_data = lk_node->lk_data;
+               if (!lk_data)
+                       return HDCP_ERROR_INVALID_INPUT;
+
+               if (lk_data->state != LINK_ST_H1_TX_LOW_VALUE_CONTENT)
+                       return HDCP_ERROR_INVALID_STATE;
+       }
+       /**
+        * if Upstream Content Control Function call this API,
+        * it changes state to ST_A0_DETERMINE_RX_HDCP_CAP automatically.
+        * HDCP library do not check CP desire.
+        */
+
+       if (!lk_data) {
+               hdcp_info("DP HDCP2.2 Session is not opened.\n");
+               return HDCP_ERROR_INVALID_INPUT;
+       }
+
+       if (rp_ready)
+               UPDATE_LINK_STATE(lk_data, LINK_ST_A7_VERIFY_RECEIVER_ID_LIST);
+       else
+               UPDATE_LINK_STATE(lk_data, LINK_ST_A0_DETERMINE_RX_HDCP_CAP);
+
+       do {
+               switch (lk_data->state) {
+               case LINK_ST_H1_TX_LOW_VALUE_CONTENT:
+                       hdcp_dplink_config(DP_HDCP22_DISABLE);
+                       return ret;
+               case LINK_ST_A0_DETERMINE_RX_HDCP_CAP:
+                       if (dplink_determine_rx_hdcp_cap(lk_data) < 0) {
+                               ret = HDCP_ERROR_RX_NOT_HDCP_CAPABLE;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A1_EXCHANGE_MASTER_KEY);
+                       break;
+               case LINK_ST_A1_EXCHANGE_MASTER_KEY:
+                       rval = dplink_exchange_master_key(lk_data);
+                       if (rval == TX_AUTH_SUCCESS) {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A2_LOCALITY_CHECK);
+                       } else {
+                               /* todo: consider connection retry */
+                               ret = HDCP_ERROR_EXCHANGE_KM;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       }
+                       break;
+               case LINK_ST_A2_LOCALITY_CHECK:
+                       rval = dplink_locality_check(lk_data);
+                       if (rval == TX_AUTH_SUCCESS) {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A3_EXCHANGE_SESSION_KEY);
+                       } else {
+                               /* todo: consider connection retry */
+                               ret = HDCP_ERROR_LOCALITY_CHECK;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       }
+                       break;
+               case LINK_ST_A3_EXCHANGE_SESSION_KEY:
+                       if (dplink_exchange_session_key(lk_data) < 0) {
+                               ret = HDCP_ERROR_EXCHANGE_KS;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A4_TEST_REPEATER);
+                       }
+                       break;
+               case LINK_ST_A4_TEST_REPEATER:
+                       if (dplink_evaluate_repeater(lk_data) == TRUE) {
+                               /* if it is a repeater, verify Rcv ID list */
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A6_WAIT_RECEIVER_ID_LIST);
+                               hdcp_info("It`s repeater link !\n");
+#if defined(CONFIG_HDCP2_FUNC_TEST_MODE)
+                               hdcp_enabled = 1;
+                               hdcp_info("it`s func test mode.\n");
+#else
+                               hdcp_enabled = 0;
+#endif
+                       } else {
+                               /* if it is not a repeater, complete authentication */
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A5_AUTHENTICATED);
+                               hdcp_info("It`s Rx link !\n");
+                               hdcp_enabled = 1;
+                       }
+                       break;
+               case LINK_ST_A5_AUTHENTICATED:
+                       /* HDCP2.2 spec defined 200ms */
+                       msleep(200);
+                       if (hdcp_enabled)
+                               hdcp_dplink_config(DP_HDCP22_ENABLE);
+                       /* Transmitter has completed the authentication protocol */
+                       ret = exynos_smc(SMC_DRM_HDCP_AUTH_INFO, DP_HDCP22_ENABLE, 0, 0);
+                       return HDCP_SUCCESS;
+               case LINK_ST_A6_WAIT_RECEIVER_ID_LIST:
+                       rval = dplink_wait_for_receiver_id_list(lk_data);
+                       if (rval == -TX_AUTH_ERROR_TIME_EXCEED) {
+                               if (retry_recvid < RECVID_WIAT_RETRY_COUNT) {
+                                       /* retry hdcp authentication in case of timeout */
+                                       hdcp_dplink_config(DP_HDCP22_DISABLE);
+                                       UPDATE_LINK_STATE(lk_data, LINK_ST_A0_DETERMINE_RX_HDCP_CAP);
+                                       hdcp_info("retry authentication. (%d)\n", retry_recvid);
+                                       retry_recvid++;
+                               } else {
+                                       hdcp_info("exceed retry count. (%d)\n", retry_recvid);
+                                       ret = HDCP_ERROR_VERIFY_RECEIVER_ID_LIST;
+                                       UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                               }
+                       } else if (rval == HDCP_SUCCESS) {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A7_VERIFY_RECEIVER_ID_LIST);
+                               retry_recvid = 0;
+                       } else {
+                               /* error on recevier id list wait */
+                               ret = HDCP_ERROR_VERIFY_RECEIVER_ID_LIST;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       }
+                       break;
+               case LINK_ST_A7_VERIFY_RECEIVER_ID_LIST:
+                       if (dplink_verify_receiver_id_list(lk_data) < 0) {
+                               ret = HDCP_ERROR_VERIFY_RECEIVER_ID_LIST;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK);
+                       }
+                       break;
+               case LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK:
+                       rval = dplink_send_receiver_id_list_ack(lk_data);
+                       if (rval < 0) {
+                               ret = HDCP_ERROR_VERIFY_RECEIVER_ID_LIST;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A9_CONTENT_STREAM_MGT);
+                       }
+                       break;
+               case LINK_ST_A9_CONTENT_STREAM_MGT:
+                       rval = dplink_stream_manage(lk_data);
+                       if (rval < 0) {
+                               ret = HDCP_ERROR_STREAM_MANAGE;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A5_AUTHENTICATED);
+                       }
+                       break;
+               default:
+                       ret = HDCP_ERROR_INVALID_STATE;
+                       UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       break;
+               }
+       } while (1);
+}
+
+/* todo: support multi link & multi session */
+int hdcp_dplink_authenticate(void)
+{
+       int ret;
+       static int retry_cnt;
+
+       auth_end_flag = HDCP_AUTH_PROCESS_ON;
+       for (; retry_cnt < HDCP_AUTH_RETRY_COUNT; retry_cnt++) {
+               if (!rp_ready) {
+                       hdcp_clear_session(ss_info.ss_id);
+                       if (hdcp_session_open(&ss_info))
+                               return -ENOMEM;
+
+                       lk_info.ss_id = ss_info.ss_id;
+                       if (hdcp_link_open(&lk_info, HDCP_LINK_TYPE_DP)) {
+                               hdcp_session_close(&ss_info);
+                               return -ENOMEM;
+                       }
+
+                       /* if hdcp is enabled, disable it while hdcp authentication */
+                       if (hdcp_dplink_is_enabled_hdcp22())
+                               hdcp_dplink_config(DP_HDCP22_DISABLE);
+
+                       dplink_clear_irqflag_all();
+               }
+
+               ret = do_dplink_auth(&lk_info);
+               if (ret == HDCP_SUCCESS) {
+                       hdcp_info("Success HDCP authenticate done.\n");
+                       retry_cnt = 0;
+                       return 0;
+               } else {
+                       /* auth_end_flag flag check */
+                       if (auth_end_flag == HDCP_AUTH_PROCESS_STOP) {
+                               hdcp_info("Stop authenticate.\n");
+                               auth_end_flag = HDCP_AUTH_PROCESS_ON;
+                               break;
+                       }
+                       /* retry */
+                       dplink_clear_irqflag_all();
+                       hdcp_err("HDCP auth failed. retry(%d)!\n", retry_cnt);
+                       retry_cnt++;
+                       if (retry_cnt == HDCP_AUTH_REAUTH_COUNT) {
+                               reset_dp_hdcp_module();
+                       } else if (retry_cnt > HDCP_AUTH_REAUTH_COUNT) {
+                               retry_cnt = 0;
+                               break;
+                       }
+               }
+       }
+
+       retry_cnt = 0;
+       return -EACCES;
+}
+
+int hdcp_dplink_auth_check(void)
+{
+       int ret;
+
+       if (drm_flag && dp_link_flag) {
+               ret = exynos_smc(SMC_DRM_HDCP_AUTH_INFO, DP_HDCP22_DISABLE, 0, 0);
+               dplink_clear_irqflag_all();
+               ret = hdcp_dplink_authenticate();
+               if (ret == 0)
+                       dp_link_flag = HDCP_AUTH_PROCESS_DONE;
+
+               return ret;
+       }
+
+       return 1;
+}
+
+int hdcp_dplink_drm_flag_check(int flag)
+{
+       int ret = 0;
+       int i = 0;
+
+       if (flag == DRM_OFF) {
+               drm_flag = DRM_OFF;
+               return 0;
+       }
+
+       for (i = 0; i < 1000; i++) {
+               drm_flag = exynos_smc(SMC_CHECK_STREAM_TYPE_FALG, 0, 0, 0);
+               if (drm_flag)
+                       break;
+               msleep(500);
+       }
+
+       if (drm_flag == DRM_SAME_STREAM_TYPE && dp_link_flag == HDCP_AUTH_PROCESS_DONE)
+               return 0;
+
+       ret = hdcp_dplink_auth_check();
+       return ret;
+}
+
+int hdcp_dplink_dp_link_flag_check(int flag)
+{
+       int ret = 0;
+
+       dp_link_flag = flag;
+#if defined(CONFIG_HDCP2_FUNC_TEST_MODE)
+       drm_flag = DRM_ON;
+#endif
+       ret = hdcp_dplink_auth_check();
+
+       return ret;
+}
+
+void hdcp_clear_session(uint32_t id)
+{
+       struct hdcp_sess_info ss;
+       struct hdcp_link_info lk;
+
+       ss.ss_id = id;
+       lk.ss_id = id;
+       lk.lk_id = id;
+       hdcp_link_close(&lk);
+       hdcp_session_close(&ss);
+}
+
+int hdcp_dplink_stream_manage(void)
+{
+       hdcp_info("stream manage updated.\n");
+       /* todo: update stream manage information */
+       return 0;
+}
+
+int hdcp_dplink_get_rxstatus(uint8_t *status)
+{
+       int ret = 0;
+
+       ret = hdcp_dplink_recv(HDCP22_MSG_RXSTATUS_R, status, sizeof(uint8_t));
+#if defined(HDCP_DEBUG)
+       hdcp_info("RxStatus: %x\n", *status);
+#endif
+       return ret;
+}
+
+int hdcp_dplink_set_paring_available(void)
+{
+       hdcp_info("pairing avaible\n");
+       return dplink_set_paring_available();
+}
+
+int hdcp_dplink_set_hprime_available(void)
+{
+       hdcp_info("h-prime avaible\n");
+       return dplink_set_hprime_available();
+}
+
+int hdcp_dplink_set_rp_ready(void)
+{
+       hdcp_info("ready avaible\n");
+       return dplink_set_rp_ready();
+}
+
+int hdcp_dplink_set_reauth(void)
+{
+       uint32_t ret = 0;
+
+       hdcp_info("reauth requested.\n");
+       ret = exynos_smc(SMC_DRM_HDCP_AUTH_INFO, DP_HDCP22_DISABLE, 0, 0);
+       return dplink_set_reauth_req();
+}
+
+int hdcp_dplink_set_integrity_fail(void)
+{
+       uint32_t ret = 0;
+
+       hdcp_info("integrity check fail.\n");
+       ret = exynos_smc(SMC_DRM_HDCP_AUTH_INFO, DP_HDCP22_DISABLE, 0, 0);
+       return dplink_set_integrity_fail();
+}
+
+int hdcp_dplink_cancel_auth(void)
+{
+       uint32_t ret = 0;
+
+       hdcp_info("Cancel authenticate.\n");
+       ret = exynos_smc(SMC_DRM_HDCP_AUTH_INFO, DP_HDCP22_DISABLE, 0, 0);
+       auth_end_flag = HDCP_AUTH_PROCESS_STOP;
+
+       return dplink_set_integrity_fail();
+}
+
+int hdcp_dplink_is_auth_state(void)
+{
+       return 0;
+       /* todo: check link auth status */
+       #if 0
+       if (lk_data->state == LINK_ST_A5_AUTHENTICATED)
+               return 1;
+       else
+               return 0;
+       #endif
+}
+
diff --git a/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.h b/drivers/soc/samsung/exynos-hdcp/dp_link/exynos-hdcp2-dplink.h
new file mode 100644 (file)
index 0000000..e2fdec0
--- /dev/null
@@ -0,0 +1,54 @@
+/* driver/soc/samsung/exynos-hdcp/dplink/exynos-hdcp2-dplink.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_DPLINK_H__
+#define __EXYNOS_HDCP2_DPLINK_H__
+
+#if defined(CONFIG_HDCP2_EMULATION_MODE)
+int dplink_emul_handler(int cmd);
+#endif
+
+enum {
+       HDCP_AUTH_PROCESS_ON   = 0x0,
+       HDCP_AUTH_PROCESS_STOP = 0x1,
+       HDCP_AUTH_PROCESS_DONE = 0x2
+};
+
+enum {
+       DRM_OFF = 0x0,
+       DRM_ON = 0x1,
+       DRM_SAME_STREAM_TYPE = 0x2      /* If the previous contents and stream_type id are the same flag */
+};
+
+/* Do hdcp2.2 authentication with DP Receiver
+ * and enable encryption if authentication is succeed.
+ * @return
+ *  - 0: Success
+ *  - ENOMEM: hdcp context open fail
+ *  - EACCES: authentication fail
+ */
+int hdcp_dplink_authenticate(void);
+
+int do_dplink_auth(struct hdcp_link_info *lk_handle);
+int hdcp_dplink_get_rxstatus(uint8_t *status);
+int hdcp_dplink_set_paring_available(void);
+int hdcp_dplink_set_hprime_available(void);
+int hdcp_dplink_set_rp_ready(void);
+int hdcp_dplink_set_reauth(void);
+int hdcp_dplink_set_integrity_fail(void);
+int hdcp_dplink_cancel_auth(void);
+int hdcp_dplink_stream_manage(void);
+int hdcp_dplink_is_auth_state(void);
+int hdcp_dplink_auth_check(void);
+int hdcp_dplink_drm_flag_check(int flag);
+int hdcp_dplink_dp_link_flag_check(int flag);
+void hdcp_clear_session(uint32_t id);
+
+extern void reset_dp_hdcp_module(void);
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-config.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-config.h
new file mode 100755 (executable)
index 0000000..0a6a449
--- /dev/null
@@ -0,0 +1,24 @@
+/* todo: include/soc/samsung/exynos-hdcp2-config.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_CONFIG_H__
+#define __EXYNOS_HDCP2_CONFIG_H__
+
+#define TEST_HDCP
+
+#undef TEST_VECTOR_1
+#define TEST_VECTOR_2
+#undef HDCP_AKE_DEBUG
+#undef HDCP_SKE_DEBUG
+#undef HDCP_TX_REPEATER_DEBUG
+
+#define HDCP_TX_VERSION_2_1
+#define HDCP_TX_LC_PRECOMPUTE_SUPPORT
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.c
new file mode 100644 (file)
index 0000000..88aedd3
--- /dev/null
@@ -0,0 +1,64 @@
+/* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/err.h>
+#include <linux/crypto.h>
+#include <linux/ctype.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+
+struct sdesc {
+       struct shash_desc shash;
+       char ctx[];
+};
+
+static struct sdesc *alloc_sdesc(struct crypto_shash *alg)
+{
+       struct sdesc *sdesc;
+       int size;
+
+       size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
+       sdesc = kmalloc(size, GFP_KERNEL);
+       if (!sdesc)
+               return ERR_PTR(-ENOMEM);
+       sdesc->shash.tfm = alg;
+       sdesc->shash.flags = 0x0;
+       return sdesc;
+}
+
+int hdcp_calc_sha1(u8 *digest, const u8 *buf, unsigned int buflen)
+{
+       struct crypto_shash *hashalg;
+       const char hash_alg[] = "sha1";
+       struct sdesc *sdesc;
+       int ret;
+
+       hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(hashalg)) {
+               pr_info("encrypted_key: could not allocate crypto %s\n",
+                       hash_alg);
+               return PTR_ERR(hashalg);
+       }
+
+       sdesc = alloc_sdesc(hashalg);
+       if (IS_ERR(sdesc)) {
+               pr_err("alloc_sdesc: can't alloc %s\n", hash_alg);
+               if (hashalg)
+                       crypto_free_shash(hashalg);
+               return PTR_ERR(sdesc);
+       }
+
+       ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
+       kfree(sdesc);
+
+       if (hashalg)
+               crypto_free_shash(hashalg);
+
+       return ret;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.h
new file mode 100644 (file)
index 0000000..38440f9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-crypto.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __EXYNOS_HDCP2_CRYPTO_H__
+#define __EXYNOS_HDCP2_CRYPTO_H__
+
+#define HDCP_SHA1_SIZE (160 / 8)
+
+int hdcp_calc_sha1(u8 *digest, const u8 *buf, unsigned int buflen);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.c
new file mode 100755 (executable)
index 0000000..73d554d
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include "exynos-hdcp2-protocol-msg.h"
+#include "exynos-hdcp2-log.h"
+
+static void OS2BN(uint32_t *pdRes, uint8_t *pbSrc, size_t uSrcLen)
+{
+       int i;
+
+       for (i = 0; i < uSrcLen; i += 4)
+               pdRes[i/4] = pbSrc[uSrcLen-i-1] ^ (pbSrc[uSrcLen-i-2]<<8)
+                               ^ (pbSrc[uSrcLen-i-3]<<16)
+                               ^ (pbSrc[uSrcLen-i-4]<<24);
+}
+
+static void BN2OS(uint8_t *pbRes, uint32_t *pdSrc, size_t uSrcLen)
+{
+       int i;
+
+       for (i = 0; i < uSrcLen; i++) {
+               pbRes[4*i+0] = (uint8_t) (pdSrc[uSrcLen-1-i] >> 24);
+               pbRes[4*i+1] = (uint8_t) (pdSrc[uSrcLen-1-i] >> 16);
+               pbRes[4*i+2] = (uint8_t) (pdSrc[uSrcLen-1-i] >>  8);
+               pbRes[4*i+3] = (uint8_t) (pdSrc[uSrcLen-1-i]);
+       }
+}
+
+static uint32_t sec_bn_Add(uint32_t *pdDst, uint32_t *pdSrc1,  size_t uSrcLen1,
+                       uint32_t *pdSrc2, size_t uSrcLen2)
+{
+       int i;
+       uint32_t carry, tmp;
+
+       for (carry = i = 0; i < uSrcLen2; i++) {
+               if ((pdSrc2[i] == 0xff) && (carry == 1))
+                       pdDst[i] = pdSrc1[i];
+               else {
+                       tmp = pdSrc2[i] + carry;
+                       pdDst[i] = pdSrc1[i] + tmp;
+                       carry = ((pdDst[i]) < tmp) ? 1 : 0;
+               }
+       }
+
+       for (i = uSrcLen2; i < uSrcLen1; i++) {
+               pdDst[i] += carry;
+               if (pdDst[i] >= carry)
+                       carry = 0;
+               else
+                       carry = 1;
+       }
+
+       return carry;
+}
+
+static int make_priv_data(uint8_t *priv_data, uint8_t *str_ctr, uint8_t *input_ctr)
+{
+       uint8_t marker_bit;
+
+       marker_bit = 0x1;
+
+       priv_data[0] = 0x0;
+       priv_data[1] = (str_ctr[0] >> 5) | marker_bit;
+       priv_data[2] = (str_ctr[0] << 2) ^ (str_ctr[1] >> 6);
+       priv_data[3] = ((str_ctr[1] << 2) ^ (str_ctr[2] >> 6)) | marker_bit;
+       priv_data[4] = (str_ctr[2] << 1) ^ (str_ctr[3] >> 7);
+       priv_data[5] = (str_ctr[3] << 1) | marker_bit;
+       priv_data[6] = 0x0;
+       priv_data[7] = (input_ctr[0] >> 3) | marker_bit;
+       priv_data[8] = (input_ctr[0] << 4) ^ (input_ctr[1] >> 4);
+       priv_data[9] = ((input_ctr[1] << 4) ^ (input_ctr[2] >> 4)) | marker_bit;
+       priv_data[10] = (input_ctr[2] << 3) ^ (input_ctr[3] >> 5);
+       priv_data[11] = ((input_ctr[3] << 3) ^ (input_ctr[4] >> 5)) | marker_bit;
+       priv_data[12] = (input_ctr[4] << 2) ^ (input_ctr[5] >> 6);
+       priv_data[13] = ((input_ctr[5] << 2) ^ (input_ctr[6] >> 6)) | marker_bit;
+       priv_data[14] = (input_ctr[6] << 1) ^ (input_ctr[7] >> 7);
+       priv_data[15] = (input_ctr[7] << 1) | marker_bit;
+
+       return 0;
+}
+
+int encrypt_packet(uint8_t *priv_data,
+       u64 input_addr, size_t input_len,
+       u64 output_addr, size_t output_len,
+       struct hdcp_tx_ctx *tx_ctx)
+{
+       uint32_t bn_str_ctr;
+       uint32_t bn_input_ctr[HDCP_INPUT_CTR_LEN / 4];
+       uint32_t tmp;
+       int ret;
+
+       /* make private data including counters for PES header */
+       make_priv_data(priv_data, tx_ctx->str_ctr, tx_ctx->input_ctr);
+
+       /* HDCP Encryption */
+       /* todo: consider 32bit address. currently only support 64bit address */
+       ret = teei_encrypt_packet((uint8_t *)input_addr, input_len,
+                       (uint8_t *)output_addr, output_len,
+                       tx_ctx->str_ctr, HDCP_STR_CTR_LEN,
+                       tx_ctx->input_ctr, HDCP_INPUT_CTR_LEN);
+       if (ret) {
+               hdcp_err("hdcp_encryption() is failed with %x\n", ret);
+               return ERR_HDCP_ENCRYPTION;
+       }
+
+       /* Update strCtr/inputCtr in tx_ctx */
+       OS2BN(&bn_str_ctr, tx_ctx->str_ctr, HDCP_STR_CTR_LEN);
+       OS2BN(bn_input_ctr, tx_ctx->input_ctr, HDCP_INPUT_CTR_LEN);
+
+       bn_str_ctr++;
+
+       tmp = input_len / 16;
+       if (input_len % 16)
+               tmp++;
+       sec_bn_Add(bn_input_ctr, bn_input_ctr, HDCP_INPUT_CTR_LEN/4, &tmp, 1);
+
+       BN2OS(tx_ctx->str_ctr, &bn_str_ctr, HDCP_STR_CTR_LEN/4);
+       BN2OS(tx_ctx->input_ctr, bn_input_ctr, HDCP_INPUT_CTR_LEN/4);
+
+#ifdef HDCP_ENC_DEBUG
+       hdcp_debug("[strCtr]:\n");
+       hdcp_hexdump(tx_ctx->str_ctr, HDCP_STR_CTR_LEN);
+
+       hdcp_debug("[inputCtr]:\n");
+       hdcp_hexdump(tx_ctx->input_ctr, HDCP_INPUT_CTR_LEN);
+#endif
+       return 0;
+}
+
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.h
new file mode 100755 (executable)
index 0000000..05dfad1
--- /dev/null
@@ -0,0 +1,22 @@
+/* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-encrypt.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_ENCRYPT_H__
+#define __EXYNOS_HDCP2_ENCRYPT_H__
+
+#include <linux/types.h>
+
+#define HDCP_PRIVATE_DATA_LEN  16 /* PES priv_data length */
+
+int encrypt_packet(uint8_t *priv_data,
+       u64 input_addr, size_t input_len,
+       u64 output_addr, size_t output_len,
+       struct hdcp_tx_ctx *tx_ctx);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-log.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-log.h
new file mode 100644 (file)
index 0000000..3c78c17
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-log.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __EXYNOS_HDCP2_LOG_H__
+#define __EXYNOS_HDCP2_LOG_H__
+
+#undef HDCP_DEBUG
+
+#ifdef HDCP_DEBUG
+#define hdcp_debug(fmt, args...)                               \
+       do {                                                    \
+               printk(KERN_ERR "[HDCP2]%s:%d: " fmt,   \
+                               __func__, __LINE__, ##args);    \
+       } while (0)
+#else
+#define hdcp_debug(fmt, args...)
+#endif
+
+#define hdcp_err(fmt, args...)                         \
+       do {                                            \
+               printk(KERN_ERR "[HDCP2]%s:%d: " fmt,   \
+                      __func__, __LINE__, ##args);     \
+       } while (0)
+
+#define hdcp_info(fmt, args...)                                \
+       do {                                            \
+               printk(KERN_INFO "[HDCP2]%s:%d: " fmt,  \
+                       __func__, __LINE__, ##args);    \
+       } while (0)
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.c
new file mode 100755 (executable)
index 0000000..7c1dfe0
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+
+void hdcp_hexdump(uint8_t *buf, size_t len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (i > 0 && !(i % 16))
+                       printk(KERN_ERR "\n");
+               printk(KERN_ERR "%02x ", buf[i]);
+       }
+       printk(KERN_ERR "\n");
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.h
new file mode 100755 (executable)
index 0000000..4f71247
--- /dev/null
@@ -0,0 +1,15 @@
+/* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-misc.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_MISC_H__
+#define __EXYNOS_HDCP2_MISC_H__
+
+void hdcp_hexdump(uint8_t *buf, size_t len);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.c
new file mode 100755 (executable)
index 0000000..4143574
--- /dev/null
@@ -0,0 +1,1070 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+
+#include "exynos-hdcp2-config.h"
+#include "exynos-hdcp2-protocol-msg.h"
+#include "exynos-hdcp2-teeif.h"
+#include "exynos-hdcp2.h"
+#include "exynos-hdcp2-log.h"
+#include "exynos-hdcp2-misc.h"
+#include "dp_link/exynos-hdcp2-dplink-protocol-msg.h"
+
+/* HDCP protocol message capulate & decapulate functions for IIA */
+static int cap_ake_init(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_ake_transmitter_info(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_ake_no_stored_km(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_ake_stored_km(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_ake_send_cert(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_ake_receiver_info(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_ake_send_rrx(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_ake_send_h_prime(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_ake_send_pairing_info(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_lc_init(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_rtt_challenge(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_lc_send_l_prime(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_rtt_ready(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_ske_send_eks(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_RepeaterAuth_send_ReceiverID_List(uint8_t *m,size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_RepeaterAuth_Send_Ack(uint8_t *m,size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_Receiver_AuthStatus(uint8_t *m,size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int cap_RepeaterAuth_Stream_Manage(uint8_t *m,size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+static int decap_RepeaterAuth_Stream_Ready(uint8_t *m,size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx);
+int (*proto_iia[])(uint8_t *, size_t *,
+                       struct hdcp_tx_ctx *,
+                       struct hdcp_rx_ctx *) = {
+       cap_ake_init,
+       decap_ake_send_cert,
+       cap_ake_no_stored_km,
+       cap_ake_stored_km,
+       decap_ake_send_rrx,
+       decap_ake_send_h_prime,
+       decap_ake_send_pairing_info,
+       cap_lc_init,
+       decap_lc_send_l_prime,
+       cap_ske_send_eks,
+       decap_RepeaterAuth_send_ReceiverID_List,
+       decap_rtt_ready,
+       cap_rtt_challenge,
+       cap_RepeaterAuth_Send_Ack,
+       cap_RepeaterAuth_Stream_Manage,
+       decap_RepeaterAuth_Stream_Ready,
+       decap_Receiver_AuthStatus,
+       cap_ake_transmitter_info,
+       decap_ake_receiver_info,
+};
+
+/* HDCP protocol message capulate & decapulate functions for DP_LINK */
+extern int (*proto_dp[])(uint8_t *, size_t *,
+                       struct hdcp_tx_ctx *,
+                       struct hdcp_rx_ctx *);
+
+int ske_generate_sessionkey(uint32_t lk_type, uint8_t *enc_skey, int share_skey)
+{
+       int ret;
+
+       ret = teei_generate_skey(lk_type,
+                       enc_skey, HDCP_SKE_SKEY_LEN,
+                       share_skey);
+       if (ret) {
+               hdcp_err("generate_session_key() is failed with %x\n", ret);
+               return ERR_GENERATE_SESSION_KEY;
+       }
+
+       return 0;
+}
+
+int ske_generate_riv(uint8_t *out)
+{
+       int ret;
+
+       ret = teei_generate_riv(out, HDCP_RTX_BYTE_LEN);
+       if (ret) {
+               hdcp_err("teei_generate_riv() is failed with %x\n", ret);
+               return ERR_GENERATE_NON_SECKEY;
+       }
+
+       return 0;
+}
+
+int lc_generate_rn(uint8_t *out, size_t out_len)
+{
+       int ret;
+
+       ret = teei_gen_rn(out, out_len);
+       if (ret) {
+               hdcp_err("lc_generate_rn() is failed with %x\n", ret);
+               return ERR_GENERATE_NON_SECKEY;
+       }
+
+       return 0;
+}
+
+int lc_make_hmac(struct hdcp_tx_ctx *tx_ctx,
+                 struct hdcp_rx_ctx *rx_ctx, uint32_t lk_type)
+{
+       int ret;
+
+       if ((rx_ctx->version != HDCP_VERSION_2_0) &&
+                       tx_ctx->lc_precomp && rx_ctx->lc_precomp) {
+               ret = teei_gen_lc_hmac(lk_type,
+                               tx_ctx->lsb16_hmac);
+               if (ret) {
+                       hdcp_err("compute_lc_whmac() is failed with %x\n", ret);
+                       return ERR_COMPUTE_LC_HMAC;
+               }
+       } else {
+               hdcp_err("LC precomp is not supported\n");
+               return ERR_COMPUTE_LC_HMAC;
+       }
+
+       return ret;
+}
+
+int lc_compare_hmac(uint8_t *rx_hmac, size_t hmac_len)
+{
+       int ret;
+
+       ret = teei_compare_lc_hmac(rx_hmac, hmac_len);
+       if (ret) {
+               hdcp_err("compare_lc_hmac_val() is failed with %x\n", ret);
+               return ERR_COMPARE_LC_HMAC;
+       }
+
+       return ret;
+}
+
+
+static int ake_generate_rtx(uint32_t lk_type, uint8_t *out, size_t out_len)
+{
+       int ret;
+
+       /* IIA Spec does not use TxCaps */
+       ret = teei_gen_rtx(lk_type, out, out_len, NULL, 0);
+       if (ret) {
+               hdcp_err("generate rtx is failed with 0x%x\n", ret);
+               return ERR_GENERATE_NON_SECKEY;
+       }
+
+       return 0;
+}
+
+static int ake_set_tx_info(struct hdcp_tx_ctx *tx_ctx)
+{
+       int ret;
+       uint8_t version[HDCP_VERSION_LEN];
+       uint8_t caps_mask[HDCP_CAPABILITY_MASK_LEN];
+
+       /* Init Version & Precomputation */
+       ret = teei_get_txinfo(version, HDCP_VERSION_LEN,
+                       caps_mask, HDCP_CAPABILITY_MASK_LEN);
+       if (ret) {
+               hdcp_err("Get Tx info is failed with 0x%x\n", ret);
+               return ERR_GET_TX_INFO;
+       }
+       hdcp_debug("Tx: version(0x%02x) Caps_maks(0x%02x%02x)\n",
+               version[0], caps_mask[0], caps_mask[1]);
+
+       tx_ctx->version = version[0];
+       tx_ctx->lc_precomp = caps_mask[1];
+
+       /* todo: check seq_num_M init position*/
+       tx_ctx->seq_num_M = 0;
+       return 0;
+}
+
+static int ake_set_rx_info(struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       uint8_t version[HDCP_VERSION_LEN];
+       uint8_t caps_mask[HDCP_CAPABILITY_MASK_LEN];
+
+
+       version[0] = rx_ctx->version;
+       caps_mask[0] = 0x00; /* reserved field in IIA spec */
+       caps_mask[1] = rx_ctx->lc_precomp;
+
+       /* Init Version & Precomputation */
+       ret = teei_set_rxinfo(version, HDCP_VERSION_LEN,
+                       caps_mask, HDCP_CAPABILITY_MASK_LEN);
+       if (ret) {
+               hdcp_err("Get Tx info is failed with 0x%x\n", ret);
+               return ERR_SET_RX_INFO;
+       }
+
+       return 0;
+}
+
+static int ake_set_rrx(uint8_t *rrx, size_t rrx_len)
+{
+       int ret;
+
+       /* set Rrx value */
+       ret = teei_set_rrx(rrx, rrx_len);
+       if (ret) {
+               hdcp_err("set rrx is failed with 0x%x\n", ret);
+               return ERR_SET_RRX;
+       }
+
+       return 0;
+}
+
+int ake_verify_cert(uint8_t *cert, size_t cert_len,
+               uint8_t *rrx, size_t rrx_len,
+               uint8_t *rx_caps, size_t rx_caps_len)
+{
+       int ret;
+
+       ret = teei_verify_cert(cert, cert_len,
+                               rrx, rrx_len,
+                               rx_caps, rx_caps_len);
+       if (ret) {
+               hdcp_err("teei_verify_cert() is failed with %x\n", ret);
+               return ERR_VERIFY_CERT;
+       }
+
+       return 0;
+}
+
+int ake_generate_masterkey(uint32_t lk_type, uint8_t *enc_mkey, size_t elen)
+{
+       int ret;
+
+       /* Generate Encrypted & Wrapped Master Key */
+       ret = teei_generate_master_key(lk_type, enc_mkey, elen);
+       if (ret) {
+               hdcp_err("generate_master_key() is failed with %x\n", ret);
+               return ERR_GENERATE_MASTERKEY;
+       }
+
+       return ret;
+}
+
+int ake_compare_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
+{
+       int ret;
+
+       ret = teei_compare_ake_hmac(rx_hmac, rx_hmac_len);
+       if (ret) {
+               hdcp_err("teei_compare_hmac() is failed with %x\n", ret);
+               return ERR_COMPUTE_AKE_HMAC;
+       }
+
+       return 0;
+}
+
+int ake_store_master_key(uint8_t *ekh_mkey, size_t ekh_mkey_len)
+{
+       int ret;
+
+       ret = teei_set_pairing_info(ekh_mkey, ekh_mkey_len);
+       if (ret) {
+               hdcp_err("teei_store_pairing_info() is failed with %x\n", ret);
+               return ERR_STORE_MASTERKEY;
+       }
+
+       return 0;
+}
+
+int ake_find_masterkey(int *found_km,
+               uint8_t *ekh_mkey, size_t ekh_mkey_len,
+               uint8_t *m, size_t m_len)
+{
+       int ret;
+
+       ret = teei_get_pairing_info(ekh_mkey, ekh_mkey_len, m, m_len);
+       if (ret) {
+               if (ret == E_HDCP_PRO_INVALID_RCV_ID) {
+                       hdcp_info("RCV id is not found\n");
+                       *found_km = 0;
+                       return 0;
+               } else {
+                       *found_km = 0;
+                       hdcp_err("teei_store_pairing_info() is failed with %x\n", ret);
+                       return ERR_FIND_MASTERKEY;
+               }
+       }
+
+       *found_km = 1;
+
+       return 0;
+}
+
+static int cap_ake_init(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       size_t rtx_len;
+       int ret;
+
+       /* Buffer Check */
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       rtx_len = HDCP_AKE_RTX_BYTE_LEN;
+
+       /* Generate rtx */
+       ret = ake_generate_rtx(HDCP_LINK_TYPE_IIA, tx_ctx->rtx, rtx_len);
+       if (ret) {
+               hdcp_err("failed to generate rtx\n");
+               return ERR_GENERATE_RTX;
+       }
+
+       /* Make Message */
+       m[0] = AKE_INIT;
+       memcpy(&m[1], tx_ctx->rtx, rtx_len);
+       *m_len = 1 + rtx_len;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("HDCP: cap_ake_init(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int cap_ake_transmitter_info(uint8_t *m, size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Set info */
+       ret = ake_set_tx_info(tx_ctx);
+       if (ret) {
+               hdcp_err("failed to set info\n");
+               return ERR_SET_INFO;
+       }
+
+
+       /* Make Message */
+       m[0] = AKE_TRANSMITTER_INFO;
+       m[1] = 0x0;
+       m[2] = 0x06;
+       m[3] = tx_ctx->version;
+       m[4] = 0x0;
+       m[5] = tx_ctx->lc_precomp;
+       *m_len = 6;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("cap_ake_transmitter_info(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int cap_ake_no_stored_km(uint8_t *m, size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t enc_mkey[HDCP_AKE_ENCKEY_BYTE_LEN];
+       int ret;
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) ||
+       (rx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Verify Certificate */
+       /* IIA spec does not define rrx and RxCaps with Cert message */
+       ret = ake_verify_cert(rx_ctx->cert, sizeof(rx_ctx->cert),
+                               NULL, 0,
+                               &(rx_ctx->repeater), HDCP_RX_CAPS_LEN);
+       if (ret)
+               /* authentication failure &
+                * aborts the authentication protocol */
+               return ret;
+
+       /* Generate/Encrypt master key */
+       ret = ake_generate_masterkey(HDCP_LINK_TYPE_IIA,
+                               enc_mkey, sizeof(enc_mkey));
+       /* Generate/Encrypt master key */
+       if (ret)
+               return ret;
+
+       /* Make Message */
+       m[0] = AKE_NO_STORED_KM;
+       memcpy(&m[1], enc_mkey, HDCP_AKE_ENCKEY_BYTE_LEN);
+       *m_len = 1 + HDCP_AKE_ENCKEY_BYTE_LEN;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("cap_ake_no_stored_km(%lu)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int cap_ake_stored_km(uint8_t *m, size_t *m_len,
+                       struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       int found_km;
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Generate/Encrypt master key */
+       ret = ake_find_masterkey(&found_km,
+                               tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN,
+                               tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
+       if (ret || !found_km) {
+               hdcp_err("find_masterkey() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* Make Message */
+       m[0] = AKE_STORED_KM;
+       memcpy(&m[1], tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN);
+       memcpy(&m[1] + HDCP_AKE_EKH_MKEY_BYTE_LEN, tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
+       *m_len = 1 + HDCP_AKE_EKH_MKEY_BYTE_LEN + HDCP_AKE_M_BYTE_LEN;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("cap_ake_stored_km(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int decap_ake_send_cert(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = check_received_msg(m, *m_len, 2 + HDCP_RX_CERT_LEN, AKE_SEND_CERT);
+       if (ret)
+               return ret;
+
+       rx_ctx->repeater = m[1];
+       memcpy(rx_ctx->cert, &m[2], HDCP_RX_CERT_LEN);
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("decap_ake_send_cert(%lu) \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int decap_ake_receiver_info(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = check_received_msg(m, *m_len, 6, AKE_RECEIVER_INFO);
+       if (ret)
+               return ret;
+
+       if ((m[2] < 6) && (m[1] == 0)) {
+               hdcp_err("AKE_Receiver_Info, send wrong length\n");
+               return ERR_WRONG_MESSAGE_LENGTH;
+       }
+
+       rx_ctx->version = m[3];
+       rx_ctx->lc_precomp = m[5];
+
+       ret = ake_set_rx_info(rx_ctx);
+       if (ret) {
+               hdcp_err("AKE set rx info failed. ret(0x%x)\n", ret);
+               return ret;
+       }
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("decap_ake_receiver_info()\n");
+       hdcp_debug("version: 0x%02x\n", rx_ctx->version);
+       hdcp_debug("HDCP lc_precomp: 0x%02x\n", rx_ctx->lc_precomp);
+#endif
+       return 0;
+}
+
+static int decap_ake_send_rrx(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       uint8_t rrx[HDCP_RRX_BYTE_LEN] = {0};
+
+       ret = check_received_msg(m, *m_len, 1 + HDCP_RRX_BYTE_LEN, AKE_SEND_RRX);
+       if (ret)
+               return ret;
+
+       memcpy(rx_ctx->rrx, &m[1], HDCP_RRX_BYTE_LEN);
+       memcpy(rrx, &m[1], HDCP_RRX_BYTE_LEN);
+       ret = ake_set_rrx(rrx, HDCP_RRX_BYTE_LEN);
+       if (ret)
+               return ret;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("decap_ake_send_rrx\n");
+       hdcp_hexdump(rx_ctx->rrx, HDCP_RRX_BYTE_LEN);
+#endif
+       return 0;
+}
+
+static int decap_ake_send_h_prime(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = check_received_msg(m, *m_len, 1 + HDCP_HMAC_SHA256_LEN,
+                               AKE_SEND_H_PRIME);
+       if (ret)
+               return ret;
+
+       /* compare H == H' */
+       ret = ake_compare_hmac(&m[1], HDCP_HMAC_SHA256_LEN);
+       if (ret)
+               return ret;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("decap_ake_send_h_prime\n");
+       hdcp_debug("given hmac:\n");
+       hdcp_hexdump(&m[1], HDCP_HMAC_SHA256_LEN);
+#endif
+       return 0;
+}
+
+static int decap_ake_send_pairing_info(uint8_t *m, size_t *m_len,
+                               struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = check_received_msg(m, *m_len, 1 + HDCP_AKE_EKH_MKEY_BYTE_LEN,
+                               AKE_SEND_PAIRING_INFO);
+       if (ret)
+               return ret;
+
+       memcpy(tx_ctx->ekh_mkey, &m[1], HDCP_AKE_EKH_MKEY_BYTE_LEN);
+
+       /* Store the Key */
+       ret = ake_store_master_key(tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN);
+       if (ret)
+               return ret;
+
+#ifdef HDCP_AKE_DEBUG
+       hdcp_debug("decap_ake_send_pairing_info(%lu)\n", *m_len);
+       hdcp_debug("rx_ctx->ekh_mkey:\n");
+       hdcp_hexdump(tx_ctx->ekh_mkey, HDCP_AKE_MKEY_BYTE_LEN);
+#endif
+
+       return 0;
+}
+
+static int cap_lc_init(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) || (rx_ctx ==
+       NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Generate rn */
+       ret = lc_generate_rn(tx_ctx->rn, HDCP_RTX_BYTE_LEN);
+       if (ret) {
+               hdcp_err("failed to generate rtx\n");
+               return ERR_GENERATE_RN;
+       }
+
+       /* Make Message */
+       m[0] = LC_INIT;
+       memcpy(&m[1], tx_ctx->rn, HDCP_RTX_BYTE_LEN);
+       *m_len = 1 + HDCP_RTX_BYTE_LEN;
+
+       if ((rx_ctx->version != HDCP_VERSION_2_0) &&
+               tx_ctx->lc_precomp &&
+               rx_ctx->lc_precomp) {
+               /* compute HMAC,
+                * return the least significant 128-bits,
+                * the most significant 128-bits wrapped */
+               ret = lc_make_hmac(tx_ctx, rx_ctx, 0); // last param 0 have to be checked, PKY
+               if (ret)
+                       return ret;
+       }
+
+#ifdef HDCP_LC_DEBUG
+       hdcp_debug("LC_Init(%u)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int cap_rtt_challenge(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Make Message */
+       m[0] = RTT_CHALLENGE;
+       memcpy(&m[1], tx_ctx->lsb16_hmac, HDCP_HMAC_SHA256_LEN / 2);
+       *m_len = 1 + (HDCP_HMAC_SHA256_LEN / 2);
+
+#ifdef HDCP_LC_DEBUG
+       hdcp_debug("RTT_Challenge(%lu)\n", *m_len);
+       hdcp_hexdump(m, *m_lne);
+#endif
+
+       return 0;
+}
+
+static int decap_lc_send_l_prime(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t *rx_hmac;
+       size_t len;
+       int ret;
+
+       /* find length depending precomputation */
+       if ((rx_ctx->version == HDCP_VERSION_2_0) || !tx_ctx->lc_precomp ||
+       !rx_ctx->lc_precomp)
+               len = HDCP_HMAC_SHA256_LEN;
+       else
+               len = HDCP_HMAC_SHA256_LEN/2;
+
+       ret = check_received_msg(m, *m_len, 1 + len, LC_SEND_L_PRIME);
+       if (ret)
+               return ret;
+
+       /* No_precomputation: compare hmac
+          precomputation: compare the most significant 128bits of L & L' */
+       rx_hmac = &m[1];
+       ret = lc_compare_hmac(rx_hmac, len);
+       if (ret)
+               return ret;
+
+#ifdef HDCP_LC_DEBUG
+       hdcp_debug("LC_Send_L_prime\n");
+       hdcp_debug("rx_hmac:\n");
+       hdcp_hexdump(rx_hmac, len);
+#endif
+
+       return 0;
+}
+
+static int decap_rtt_ready(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = check_received_msg(m, *m_len, 1, RTT_READY);
+       if (ret)
+               return ret;
+
+#ifdef HDCP_LC_DEBUG
+       hdcp_debug("RTT_Ready\n");
+#endif
+
+       return 0;
+}
+
+static int cap_ske_send_eks(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       uint8_t enc_skey[HDCP_AKE_MKEY_BYTE_LEN];
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) ||
+       (rx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Generate riv */
+       if (!tx_ctx->share_skey) {
+               ret = ske_generate_riv(tx_ctx->riv);
+               if (ret)
+                       return ERR_GENERATE_RIV;
+       }
+
+       /* Generate encrypted Session Key */
+       ret = ske_generate_sessionkey(0, enc_skey, tx_ctx->share_skey);
+       if (ret)
+               return ret;
+
+       /* Make Message */
+       m[0] = SKE_SEND_EKS;
+       memcpy(&m[1], enc_skey, HDCP_AKE_MKEY_BYTE_LEN);
+       memcpy(&m[1 + HDCP_AKE_MKEY_BYTE_LEN], tx_ctx->riv, HDCP_RTX_BYTE_LEN);
+       *m_len = 1 + HDCP_AKE_MKEY_BYTE_LEN + HDCP_RTX_BYTE_LEN;
+
+#ifdef HDCP_SKE_DEBUG
+       hdcp_debug("SKE_Send_Eks(%lu)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+#ifdef TEST_HDCP_V2_0
+int parse_rcvid_list(uint8_t *msg, struct hdcp_tx_ctx *tx_ctx)
+{
+        /* get PRE META values */
+        tx_ctx->rcv_list.devs_exd = (uint8_t)*msg;
+        tx_ctx->rcv_list.cascade_exd = (uint8_t)*(msg + 1);
+
+        /* get META values */
+        msg += HDCP_RP_RCV_LIST_PRE_META_LEN;
+        tx_ctx->rcv_list.devs_count = (uint8_t)*msg;
+        tx_ctx->rcv_list.depth = (uint8_t)*(msg + 1);
+        memcpy(tx_ctx->rcv_list.hmac_prime, (uint8_t *)(msg + 2), 32);
+
+        /* get receiver ID list */
+        msg += 34;
+        memcpy(tx_ctx->rcv_list.rcv_id, msg, tx_ctx->rcv_list.devs_count * HDCP_RCV_ID_LEN);
+
+       return 0;
+}
+
+void convert_rcvlist2authmsg(struct hdcp_rcvlist *rcv_list, uint8_t *src_msg, size_t *msg_len)
+{
+        int i;
+        *msg_len = 0;
+
+        for (i = 0; i < rcv_list->devs_count; i++) {
+                memcpy(src_msg + *msg_len, rcv_list->rcv_id[i], HDCP_RCV_ID_LEN);
+                *msg_len += HDCP_RCV_ID_LEN;
+        }
+
+        /* concatinate DEPTH */
+        memcpy(src_msg + *msg_len, &rcv_list->depth, 1);
+        *msg_len += 1;
+
+        /* concatinate DEVICE COUNT */
+        memcpy(src_msg + *msg_len, &rcv_list->devs_count, 1);
+        *msg_len += 1;
+
+        /* concatinate MAX DEVS EXCEEDED */
+        memcpy(src_msg + *msg_len, &rcv_list->devs_exd, 1);
+        *msg_len += 1;
+
+        /* concatinate MAX CASCADE EXCEEDED */
+        memcpy(src_msg + *msg_len, &rcv_list->cascade_exd, 1);
+        *msg_len += 1;
+}
+#else
+int parse_rcvid_list(uint8_t *msg, struct hdcp_tx_ctx *tx_ctx)
+{
+        /* get PRE META values */
+        tx_ctx->rpauth_info.devs_exd = (uint8_t)*msg;
+        tx_ctx->rpauth_info.cascade_exd = (uint8_t)*(msg + 1);
+
+        /* get META values */
+        msg += HDCP_RP_RCV_LIST_PRE_META_LEN;
+        tx_ctx->rpauth_info.devs_count = (uint8_t)*msg;
+        tx_ctx->rpauth_info.depth = (uint8_t)*(msg + 1);
+        tx_ctx->rpauth_info.hdcp2_down = (uint8_t)*(msg + 2);
+        tx_ctx->rpauth_info.hdcp1_down = (uint8_t)*(msg + 3);
+        memcpy(tx_ctx->rpauth_info.seq_num_v, (uint8_t *)(msg + 4), 3);
+        memcpy(tx_ctx->rpauth_info.v_prime, (uint8_t *)(msg + 7), 16);
+
+        /* get receiver ID list */
+        msg += HDCP_RP_RCV_LIST_META_LEN;
+       if (tx_ctx->rpauth_info.devs_count > HDCP_RCV_DEVS_COUNT_MAX) {
+               hdcp_err("invalid DEVS count (%d)\n", tx_ctx->rpauth_info.devs_count);
+               return -1;
+       }
+
+        memcpy(tx_ctx->rpauth_info.u_rcvid.arr, msg, tx_ctx->rpauth_info.devs_count * HDCP_RCV_ID_LEN);
+
+       return 0;
+}
+
+void convert_rcvlist2authmsg(struct hdcp_rpauth_info *rpauth_info, uint8_t *src_msg, size_t *msg_len)
+{
+        int i;
+        *msg_len = 0;
+
+        for (i = 0; i < rpauth_info->devs_count; i++) {
+                memcpy(src_msg + *msg_len, rpauth_info->u_rcvid.arr[i], HDCP_RCV_ID_LEN);
+                *msg_len += HDCP_RCV_ID_LEN;
+        }
+
+        /* concatinate DEPTH */
+        memcpy(src_msg + *msg_len, &rpauth_info->depth, 1);
+        *msg_len += 1;
+
+        /* concatinate DEVICE COUNT */
+        memcpy(src_msg + *msg_len, &rpauth_info->devs_count, 1);
+        *msg_len += 1;
+
+        /* concatinate MAX DEVS EXCEEDED */
+        memcpy(src_msg + *msg_len, &rpauth_info->devs_exd, 1);
+        *msg_len += 1;
+
+        /* concatinate MAX CASCADE EXCEEDED */
+        memcpy(src_msg + *msg_len, &rpauth_info->cascade_exd, 1);
+        *msg_len += 1;
+
+        /* concatinate HDCP2 REPEATER DOWNSTREAM */
+        memcpy(src_msg + *msg_len, &rpauth_info->hdcp2_down, 1);
+        *msg_len += 1;
+
+        /* concatinate HDCP1 DEVICE DOWNSTREAM */
+        memcpy(src_msg + *msg_len, &rpauth_info->hdcp1_down, 1);
+        *msg_len += 1;
+
+        /* concatinate seq_num_v */
+        memcpy(src_msg + *msg_len, &rpauth_info->seq_num_v, 3);
+        *msg_len += 3;
+}
+#endif
+
+static int decap_RepeaterAuth_send_ReceiverID_List(uint8_t *m,
+               size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       size_t hmac_prime_len;
+       size_t msg_len;
+       uint8_t source_msg[256];
+       int ret;
+
+       if (rx_ctx->version == HDCP_VERSION_2_0)
+               hmac_prime_len = HDCP_HMAC_SHA256_LEN;
+       else
+               hmac_prime_len = HDCP_HMAC_SHA256_LEN/2;
+
+       ret = check_received_msg(m, *m_len, 0, REPEATERAUTH_SEND_RECEIVERID_LIST);
+       if (ret)
+               return ret;
+
+       if (parse_rcvid_list(m + 1, tx_ctx))
+               return -1;
+
+       convert_rcvlist2authmsg(&tx_ctx->rpauth_info, source_msg, &msg_len);
+
+       ret = teei_set_rcvlist_info(NULL, NULL, tx_ctx->rpauth_info.v_prime,
+                       source_msg, tx_ctx->rpauth_info.v, &(tx_ctx->rpauth_info.valid));
+
+       tx_ctx->rpauth_info.valid = 0;
+       if (ret) {
+               tx_ctx->rpauth_info.valid = 1;
+               return ret;
+       }
+
+#ifdef HDCP_TX_REPEATER_DEBUG
+       hdcp_debug("decap_RepeaterAuth_send_ReceiverID_List valid (%u)\n", tx_ctx->rpauth_info.valid);
+       hdcp_hexdump(tx_ctx->rpauth_info.v, HDCP_RP_HMAC_V_LEN / 2);
+#endif
+       return 0;
+}
+
+static int cap_RepeaterAuth_Send_Ack(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       /* Make Message */
+       m[0] = REPEATERAUTH_SEND_ACK;
+       if(tx_ctx->rpauth_info.valid == 0)
+               memcpy(&m[1], tx_ctx->rpauth_info.v, HDCP_RP_HMAC_V_LEN / 2);
+       else
+               return ERR_SEND_ACK;
+
+       *m_len = 1 + (HDCP_RP_HMAC_V_LEN / 2);
+
+#ifdef HDCP_TX_REPEATER_DEBUG
+       hdcp_debug("make_RepeaterAuth_Send_Ack(%u)\n", *m_len);
+       hdcp_hexdump(m, *m_len);
+#endif
+       return 0;
+}
+
+static int decap_Receiver_AuthStatus(uint8_t *m,
+               size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = check_received_msg(m, (int)*m_len, 0,  RECEIVER_AUTHSTATUS);
+       if (ret)
+               return ret;
+
+       tx_ctx->rp_reauth = m[3];
+#ifdef HDCP_TX_REPEATER_DEBUG
+       hdcp_debug("get_Receiver_AuthStatus(%u)\n", *m_len);
+       hdcp_debug("receiver reauth req: %u\n", tx_ctx->rp_reauth);
+#endif
+       return 0;
+}
+
+int cap_RepeaterAuth_Stream_Manage(uint8_t *m, size_t *m_len,
+               struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int i;
+       uint8_t *dst;
+       uint16_t stmp;
+       uint32_t itmp;
+
+       if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
+               return ERR_WRONG_BUFFER;
+
+       if (tx_ctx->stream_ctrl.str_num > HDCP_TX_REPEATER_MAX_STREAM)
+               return ERR_EXCEED_MAX_STREAM;
+
+       /* Make Message */
+       m[0] = REPEATERAUTH_STREAM_MANAGE;
+       itmp = htonl(tx_ctx->seq_num_M);
+       memcpy(&m[1], (uint8_t *)&itmp + 1, 3);
+       stmp = htons(tx_ctx->stream_ctrl.str_num);
+       memcpy(&m[4], &stmp, sizeof(stmp));
+
+       for (i = 0; i < tx_ctx->stream_ctrl.str_num; i++) {
+               dst = (uint8_t *)(&m[6] + STREAM_INFO_SIZE * i);
+               itmp = htonl(tx_ctx->stream_ctrl.str_info[i].ctr);
+               memcpy(dst, &itmp, sizeof(itmp));
+               stmp = htons(tx_ctx->stream_ctrl.str_info[i].pid);
+               memcpy(dst + sizeof(itmp), &stmp, sizeof(stmp));
+               dst[STREAM_INFO_SIZE - 1] = tx_ctx->stream_ctrl.str_info[i].type;
+       }
+
+       *m_len = HDCP_PROTO_MSG_ID_LEN + STREAM_ELEM_SIZE + STREAM_INFO_SIZE * i;
+
+       /* seq_num_M++ */
+       tx_ctx->seq_num_M++;
+
+       /* save message to make M */
+       memcpy(tx_ctx->strmsg, &m[6], *m_len - 6);
+       memcpy(tx_ctx->strmsg + *m_len - 6, &m[1], 3);
+       tx_ctx->strmsg_len = *m_len - 3;
+
+#ifdef HDCP_TX_REPEATER_DEBUG
+       hdcp_debug("strmsg(len: %d): \n", tx_ctx->strmsg_len);
+       hdcp_hexdump(tx_ctx->strmsg, tx_ctx->strmsg_len);
+
+       hdcp_debug("message(len: %d): \n", *m_len);
+       hdcp_hexdump(m, *m_len);
+
+       hdcp_debug("seq_num_M: %d\n", tx_ctx->seq_num_M);
+#endif
+       return 0;
+}
+
+int decap_RepeaterAuth_Stream_Ready(uint8_t *m, size_t *m_len,
+               struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret = 0;
+       /* Not support yet*/
+#if 0
+       ret = check_received_msg(m, *m_len, 1 + HDCP_HMAC_SHA256_LEN, REPEATERAUTH_STREAM_READY);
+       if (ret)
+               return ret;
+
+       /* compute M and compare M == M' */
+       ret = teei_verify_m_prime(&m[1], tx_ctx->strmsg, tx_ctx->strmsg_len);
+       if (ret)
+               return ret;
+#endif
+       return ret;
+}
+
+int cap_protocol_msg(uint8_t msg_id,
+               uint8_t *msg,
+               size_t *msg_len,
+               uint32_t lk_type,
+               struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       /* todo: check upper boundary */
+       int ret = 0;
+       int (**proto_func)(uint8_t *, size_t *,
+                       struct hdcp_tx_ctx *,
+                       struct hdcp_rx_ctx *);
+
+       if (lk_type == HDCP_LINK_TYPE_IIA)
+               proto_func = proto_iia;
+#if defined(CONFIG_HDCP2_DP_ENABLE)
+       else if(lk_type == HDCP_LINK_TYPE_DP)
+               proto_func = proto_dp;
+#endif
+       else {
+               hdcp_err("invalid link type(%d)\n", lk_type);
+               return -1;
+       }
+
+       if (msg_id > 1) {
+               ret = proto_func[msg_id - 2](msg, msg_len, tx_ctx, rx_ctx);
+               return ret;
+       }
+       else
+               return -1;
+}
+
+int decap_protocol_msg(uint8_t msg_id,
+               uint8_t *msg,
+               size_t msg_len,
+               uint32_t lk_type,
+               struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret = 0;
+       int (**proto_func)(uint8_t *, size_t *,
+                       struct hdcp_tx_ctx *,
+                       struct hdcp_rx_ctx *);
+
+       /* todo: check upper boundary */
+       if (lk_type == HDCP_LINK_TYPE_IIA)
+               proto_func = proto_iia;
+#if defined(CONFIG_HDCP2_DP_ENABLE)
+       else if(lk_type == HDCP_LINK_TYPE_DP)
+               proto_func = proto_dp;
+#endif
+       else {
+               hdcp_err("HDCP: invalid link type(%d)\n", lk_type);
+               return -1;
+       }
+
+       if (msg_id > 1) {
+               ret = proto_func[msg_id - 2](msg, &msg_len, tx_ctx, rx_ctx);
+               return ret;
+       }
+
+       else
+               return -1;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.h
new file mode 100755 (executable)
index 0000000..fe36de9
--- /dev/null
@@ -0,0 +1,233 @@
+/* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __EXYNOS_HDCP2_PROTOCOL_MSG_H__
+#define __EXYNOS_HDCP2_PROTOCOL_MSG_H__
+
+#include <linux/types.h>
+#include "exynos-hdcp2-teeif.h"
+
+/* Error */
+#define ERR_TLC_CONNECT                        0x1001
+#define ERR_WRONG_BUFFER               0x1002
+#define ERR_WRONG_MESSAGE_LENGTH       0x1003
+#define ERR_WRONG_MESSAGE_ID           0x1004
+#define ERR_GENERATE_NON_SECKEY                0x1005
+#define ERR_FILE_OPEN                  0x1006
+
+#define ERR_GENERATE_RTX               0x2001
+#define ERR_VERIFY_CERT                        0x2002
+#define ERR_GENERATE_MASTERKEY         0x2003
+#define ERR_COMPUTE_AKE_HMAC           0x2004
+#define ERR_COMPARE_AKE_HMAC           0x2005
+#define ERR_STORE_MASTERKEY            0x2006
+#define ERR_CHECK_SRM                  0x2007
+#define ERR_WRAP_SRM                   0x2008
+#define ERR_SET_INFO                   0x2009
+#define ERR_MEMORY_ALLOCATION          0x200A
+#define ERR_GET_TX_INFO                        0x200B
+#define ERR_SET_RX_INFO                        0x200C
+#define ERR_SET_RRX                    0x200D
+#define ERR_FIND_MASTERKEY             0x200E
+
+#define ERR_GENERATE_RN                        0x3001
+#define ERR_COMPUTE_LC_HMAC            0x3002
+#define ERR_COMPARE_LC_HMAC            0x3003
+
+#define ERR_GENERATE_RIV               0x4001
+#define ERR_GENERATE_SESSION_KEY       0x4002
+
+#define ERR_HDCP_ENCRYPTION            0x5001
+
+#define ERR_RP_VERIFY_RCVLIST           0x6001
+#define ERR_EXCEED_MAX_STREAM           0x6002
+#define ERR_VALIDATE_M                 0x6003
+#define ERR_SEND_ACK                   0x6004
+#define ERR_RP_GEN_STREAM_MG                   0x6005
+#define ERR_RP_INVALID_M_PRIME                 0x6006
+
+#define HDCP_RP_RCV_LIST_PRE_META_LEN   2
+#define HDCP_RP_RCV_LIST_META_LEN       23
+
+#define HDCP_PROTO_MSG_ID_LEN          1
+#define HDCP_TX_REPEATER_MAX_STREAM     32
+#define STREAM_ELEM_SIZE               5
+#define STREAM_INFO_SIZE               7
+#define STREAM_MAX_LEN                 1024
+
+enum {
+       HDCP_VERSION_2_0 = 0x00,
+       HDCP_VERSION_2_1 = 0x01,
+       HDCP_VERSION_2_2 = 0x01,
+};
+
+enum {
+       LC_PRECOMPUTE_NOT_SUPPORT = 0x00,
+       LC_PRECOMPUTE_SUPPORT = 0x01,
+};
+
+enum msg_id {
+       AKE_INIT = 2,
+       AKE_SEND_CERT,
+       AKE_NO_STORED_KM,
+       AKE_STORED_KM,
+       AKE_SEND_RRX,
+       AKE_SEND_H_PRIME,
+       AKE_SEND_PAIRING_INFO,
+       LC_INIT,
+       LC_SEND_L_PRIME,
+       SKE_SEND_EKS,
+       REPEATERAUTH_SEND_RECEIVERID_LIST,
+       RTT_READY,
+       RTT_CHALLENGE,
+       REPEATERAUTH_SEND_ACK,
+       REPEATERAUTH_STREAM_MANAGE,
+       REPEATERAUTH_STREAM_READY,
+       RECEIVER_AUTHSTATUS,
+       AKE_TRANSMITTER_INFO,
+       AKE_RECEIVER_INFO,
+};
+
+struct stream_info {
+       uint8_t type;
+       uint16_t pid;
+       uint32_t ctr;
+};
+
+struct contents_info {
+       uint8_t str_num;
+       struct stream_info str_info[HDCP_TX_REPEATER_MAX_STREAM];
+};
+
+struct hdcp_rpauth_info {
+       uint8_t devs_exd;
+       uint8_t cascade_exd;
+       uint8_t devs_count;
+       uint8_t depth;
+       uint8_t hdcp2_down;
+       uint8_t hdcp1_down;
+       uint8_t rx_info[HDCP_RP_RX_INFO_LEN];
+       uint8_t seq_num_v[HDCP_RP_SEQ_NUM_V_LEN];
+       uint8_t v_prime[HDCP_RP_HMAC_V_LEN / 2];
+       union {
+               uint8_t arr[HDCP_RCV_DEVS_COUNT_MAX][HDCP_RCV_ID_LEN];
+               uint8_t lst[HDCP_RP_RCVID_LIST_LEN];
+       } u_rcvid;
+       /* repeater auth result */
+       uint8_t v[HDCP_RP_HMAC_V_LEN / 2];
+       uint8_t valid;
+};
+
+struct dp_stream_info {
+       uint16_t stream_num;
+       uint8_t streamid[HDCP_RP_MAX_STREAMID_NUM];
+       uint8_t seq_num_m[HDCP_RP_SEQ_NUM_M_LEN];
+       uint8_t k[HDCP_RP_K_LEN];
+       uint8_t streamid_type[HDCP_RP_MAX_STREAMID_TYPE_LEN];
+};
+
+union stream_manage {
+       struct dp_stream_info dp;
+       /* todo: add IIA */
+};
+
+struct hdcp_tx_ctx {
+       uint8_t version;
+       uint8_t lc_precomp;
+
+       /* master key */
+       uint8_t wrap_mkey[HDCP_AKE_WKEY_BYTE_LEN];
+       uint8_t caps[HDCP_CAPS_BYTE_LEN];
+       uint8_t ekh_mkey[HDCP_AKE_EKH_MKEY_BYTE_LEN];
+       uint8_t m[HDCP_AKE_M_BYTE_LEN];
+
+       uint8_t rtx[HDCP_AKE_RTX_BYTE_LEN];
+       uint8_t rn[HDCP_AKE_RTX_BYTE_LEN];
+       uint8_t riv[HDCP_AKE_RTX_BYTE_LEN];
+       uint8_t lsb16_hmac[HDCP_HMAC_SHA256_LEN / 2];
+
+       /* session key */
+       uint8_t str_ctr[HDCP_STR_CTR_LEN];
+       uint8_t input_ctr[HDCP_INPUT_CTR_LEN];
+
+       /* receiver list */
+       struct hdcp_rpauth_info rpauth_info;
+
+       /* stream manage info */
+       union stream_manage strm;
+
+       /* repeater reauth request */
+       uint8_t rp_reauth;
+
+       /* todo: move stream_ctrl */
+       struct contents_info stream_ctrl;
+
+       int share_skey;
+       uint32_t seq_num_M;
+       uint8_t strmsg[HDCP_TX_REPEATER_MAX_STREAM * 8];
+       size_t strmsg_len;
+};
+
+struct hdcp_rx_ctx {
+       uint8_t version;
+       uint8_t lc_precomp;
+       uint8_t repeater;
+       uint8_t caps[HDCP_CAPS_BYTE_LEN]; /* only for DP */
+       uint8_t receiver_id[RECEIVER_ID_BYTE_LEN];
+       uint8_t rrx[HDCP_AKE_RTX_BYTE_LEN];
+       uint8_t cert[HDCP_RX_CERT_LEN];
+};
+
+int cap_protocol_msg(uint8_t msg_id,
+       uint8_t *msg,
+       size_t *msg_len,
+       uint32_t lk_type,
+       struct hdcp_tx_ctx *tx_ctx,
+       struct hdcp_rx_ctx *rx_ctx);
+
+int decap_protocol_msg(uint8_t msg_id,
+       uint8_t *msg,
+       size_t msg_len,
+       uint32_t lk_type,
+       struct hdcp_tx_ctx *tx_ctx,
+       struct hdcp_rx_ctx *rx_ctx);
+
+int ake_find_masterkey(int *found_km,
+               uint8_t *ekh_mkey, size_t ekh_mkey_len,
+               uint8_t *m, size_t m_len);
+
+#define check_received_msg(m, m_len, len, func_id) \
+       ((m == NULL) ? ERR_WRONG_BUFFER : \
+       ((m_len < len) ? ERR_WRONG_MESSAGE_LENGTH : \
+       ((m[0] != func_id) ? ERR_WRONG_MESSAGE_ID : 0)))
+
+#endif
+
+int ake_verify_cert(uint8_t *cert, size_t cert_len,
+               uint8_t *rrx, size_t rrx_len,
+               uint8_t *rx_caps, size_t rx_caps_len);
+int ake_generate_masterkey(uint32_t lk_type,
+                       uint8_t *enc_mkey, size_t elen);
+int ake_make_hmac(uint8_t *whmac, uint8_t *wrap_mkey,
+                       uint8_t *rtx, uint8_t repeater,
+                       uint8_t *hmac_append);
+int ake_make_hmac_with_caps(uint8_t *whmac, uint8_t *wrap_mkey,
+                       uint8_t *rtx, uint8_t *rrx,
+                       uint8_t *tx_caps, uint8_t *rx_caps);
+int ake_compare_hmac(uint8_t *rx_hmac, size_t rx_hmac_len);
+int ake_store_master_key(uint8_t *ekh_mkey, size_t ekh_mkey_len);
+
+int lc_make_hmac(struct hdcp_tx_ctx *tx_ctx,
+                 struct hdcp_rx_ctx *rx_ctx, uint32_t lk_type);
+int lc_generate_rn(uint8_t *out, size_t out_len);
+int lc_compare_hmac(uint8_t *rx_hmac, size_t hmac_len);
+
+int ske_generate_riv(uint8_t *out);
+int ske_generate_sessionkey(uint32_t lk_type, uint8_t *enc_skey,
+                       int share_skey);
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-session.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-session.c
new file mode 100755 (executable)
index 0000000..7250c7c
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-tx-session.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/slab.h>
+
+#include "exynos-hdcp2.h"
+#include "exynos-hdcp2-log.h"
+
+/**
+ * generate data for a session data
+ */
+
+struct hdcp_session_data *hdcp_session_data_create(void)
+{
+       struct hdcp_session_data *new_ss_data = NULL;
+       static int count = 0; /* TODO change session id creation method */
+
+       new_ss_data = (struct hdcp_session_data *)kzalloc(sizeof(struct hdcp_session_data), GFP_KERNEL);
+       if (!new_ss_data) {
+               return NULL;
+       }
+
+       /* init session data */
+       new_ss_data->id = count++;
+
+       new_ss_data->wrap_skey_store = WRAP_SKEY_EMPTY;
+       memset(new_ss_data->riv, 0x00, sizeof(new_ss_data->riv));
+       hdcp_link_list_init(&(new_ss_data->ln));
+
+       new_ss_data->state = SESS_ST_INIT;
+
+       return new_ss_data;
+}
+
+/**
+ * destroy a session data
+ */
+void hdcp_session_data_destroy(struct hdcp_session_data **ss_data)
+{
+        if (!(*ss_data) || !ss_data)
+                return;
+
+        /* clear session key and riv */
+        memset((*ss_data)->wrap_skey, 0x00, sizeof((*ss_data)->wrap_skey));
+        memset((*ss_data)->riv, 0x00, sizeof((*ss_data)->riv));
+        /* clear link list */
+        hdcp_link_list_destroy(&((*ss_data)->ln));
+
+        if (*ss_data) {
+                kfree(*ss_data);
+                *ss_data = NULL;
+        }
+}
+
+/**
+ * generate data for a link data
+ */
+struct hdcp_link_data *hdcp_link_data_create(void)
+{
+       struct hdcp_link_data *new_lk_data = NULL;
+       static int count = 0; /* TODO: change link id creation method */
+
+       new_lk_data = (struct hdcp_link_data *)kzalloc(sizeof(struct hdcp_link_data), GFP_KERNEL);
+       if (!new_lk_data) {
+               return NULL;
+       }
+
+       /* init link data */
+       new_lk_data->id = count++;
+       new_lk_data->state = LINK_ST_INIT;
+
+       /* set HDCP version */
+#ifdef HDCP_TX_VERSION_2_2
+       new_lk_data->tx_ctx.version = HDCP_VERSION_2_2;
+#endif
+
+       return new_lk_data;
+}
+
+/**
+ * destroy a link data
+ */
+void hdcp_link_data_destroy(struct hdcp_link_data **lk_data)
+{
+       if (!(*lk_data) || !lk_data)
+               return;
+
+       (*lk_data)->ss_ptr = NULL;
+
+       if (*lk_data) {
+               kfree(*lk_data);
+               *lk_data = NULL;
+       }
+}
+
+/**
+ * init a Session list
+ * @ss_list: list head to add it after
+ */
+void hdcp_session_list_init(struct hdcp_session_list *ss_list)
+{
+        struct hdcp_session_node *ss_head;
+
+        if (!ss_list)
+                return;
+
+       /* hdcp session list mutex init */
+       mutex_init(&ss_list->ss_mutex);
+
+        ss_head = &(ss_list->hdcp_session_head);
+        ss_head->next = ss_head;
+        ss_head->prev = ss_head;
+        ss_head->ss_data = NULL;
+}
+
+/**
+ * add a new entry to the Session list
+ * @new_ent: new entry to be added
+ * @ss_list: list head to add it after
+ *
+ * Insert a new entry after the specified head
+ */
+void hdcp_session_list_add(struct hdcp_session_node *new_ent, struct hdcp_session_list *ss_list)
+{
+       struct hdcp_session_node *ss_head;
+
+       if (!new_ent || !ss_list)
+               return;
+
+       mutex_lock(&(ss_list->ss_mutex));
+       ss_head = &(ss_list->hdcp_session_head);
+
+       ss_head->next->prev = new_ent;
+       new_ent->next = ss_head->next;
+
+       new_ent->prev = ss_head;
+       ss_head->next = new_ent;
+       mutex_unlock(&(ss_list->ss_mutex));
+
+       return;
+
+}
+
+/**
+ * delete a entry form the Session list
+ * @del_ent: a entry to be deleted
+ * @ss_list: session list to remove the session node
+ */
+void hdcp_session_list_del(struct hdcp_session_node *del_ent, struct hdcp_session_list *ss_list)
+{
+       if (!del_ent || !ss_list)
+               return;
+
+       mutex_lock(&ss_list->ss_mutex);
+       del_ent->prev->next = del_ent->next;
+       del_ent->next->prev = del_ent->prev;
+       mutex_unlock(&ss_list->ss_mutex);
+}
+
+/**
+ * print all entries in the Session list
+ * @ss_list: session list to print all nodes
+ */
+void hdcp_session_list_print_all(struct hdcp_session_list *ss_list)
+{
+       struct hdcp_session_node *pos;
+       struct hdcp_session_node *ss_head;
+
+       if (!ss_list)
+               return;
+
+       mutex_lock(&ss_list->ss_mutex);
+       ss_head = &(ss_list->hdcp_session_head);
+       for (pos = ss_head->next; pos != ss_head && pos != NULL; pos = pos->next)
+               hdcp_info("SessionID: %d\n", pos->ss_data->id);
+       mutex_unlock(&ss_list->ss_mutex);
+}
+
+/**
+ * Find an entry from the Session list
+ * @id: session id to find session node
+ * @ss_list: session list contain the session node
+ */
+struct hdcp_session_node *hdcp_session_list_find(uint32_t id, struct hdcp_session_list *ss_list)
+{
+       struct hdcp_session_node *pos;
+       struct hdcp_session_node *ss_head;
+
+
+       if (!ss_list)
+               return NULL;
+
+       mutex_lock(&ss_list->ss_mutex);
+       ss_head = &ss_list->hdcp_session_head;
+       for (pos = ss_head->next; pos != ss_head && pos != NULL; pos = pos->next) {
+               if (pos->ss_data->id == id) {
+                       mutex_unlock(&ss_list->ss_mutex);
+                       return pos;
+               }
+       }
+       mutex_unlock(&ss_list->ss_mutex);
+
+       return NULL;
+}
+
+/**
+ * close all links in the session and remove all session nodes
+ * @ss_list: session list to remove all
+ */
+void hdcp_session_list_destroy(struct hdcp_session_list *ss_list)
+{
+        struct hdcp_session_node *pos;
+        struct hdcp_session_node *ss_head;
+
+        if (!ss_list)
+                return;
+
+        mutex_lock(&ss_list->ss_mutex);
+        ss_head = &ss_list->hdcp_session_head;
+        for (pos = ss_head->next; pos != ss_head && pos != NULL;) {
+                if (pos) {
+                        /* remove session node from the list*/
+                        pos->prev->next = pos->next;
+                        pos->next->prev = pos->prev;
+
+                        /* remove session data */
+                        /* TODO : remove all links */
+                        hdcp_session_data_destroy(&pos->ss_data);
+                        if (pos)
+                                kfree(pos);
+                        pos = ss_head->next;
+                }
+        }
+        ss_head->next = ss_head;
+        ss_head->prev = ss_head;
+        mutex_unlock(&ss_list->ss_mutex);
+}
+
+/**
+ * init a Link list
+ * @lk_list: list head to add it after
+ */
+void hdcp_link_list_init(struct hdcp_link_list *lk_list)
+{
+       struct hdcp_link_node *lk_head;
+
+       if (!lk_list)
+               return;
+
+       /* initialize link list mutex */
+       mutex_init(&lk_list->lk_mutex);
+
+       mutex_lock(&lk_list->lk_mutex);
+       lk_head = &(lk_list->hdcp_link_head);
+       lk_head->next = lk_head;
+       lk_head->prev = lk_head;
+       lk_head->lk_data = NULL;
+       mutex_unlock(&lk_list->lk_mutex);
+}
+
+/**
+ * add a new entry to the Link list
+ * @new_ent: new entry to be added
+ * @ss_list: list head to add it after
+ *
+ * Insert a new entry after the specified head
+ */
+void hdcp_link_list_add(struct hdcp_link_node *new_ent, struct hdcp_link_list *lk_list)
+{
+       struct hdcp_link_node *lk_head;
+
+       if (!new_ent || !lk_list)
+               return;
+
+       mutex_lock(&lk_list->lk_mutex);
+       lk_head = &(lk_list->hdcp_link_head);
+       lk_head->next->prev = new_ent;
+       new_ent->next = lk_head->next;
+       new_ent->prev = lk_head;
+       lk_head->next = new_ent;
+       mutex_unlock(&lk_list->lk_mutex);
+}
+
+/**
+ * delete a entry form the Link list
+ * @del_ent: a entry to be deleted
+ * @ss_list: session list to remove the session node
+ */
+void hdcp_link_list_del(struct hdcp_link_node *del_ent, struct hdcp_link_list *lk_list)
+{
+       if (!del_ent || !lk_list)
+               return;
+
+       mutex_lock(&lk_list->lk_mutex);
+       del_ent->prev->next = del_ent->next;
+       del_ent->next->prev = del_ent->prev;
+       mutex_unlock(&lk_list->lk_mutex);
+}
+
+/**
+ * print all entries in the Link list
+ * @ss_list: session list to print all nodes
+ */
+void hdcp_link_list_print_all(struct hdcp_link_list *lk_list)
+{
+       struct hdcp_link_node *pos;
+       struct hdcp_link_node *lk_head;
+
+       if (!lk_list)
+               return;
+
+       mutex_lock(&lk_list->lk_mutex);
+       lk_head = &lk_list->hdcp_link_head;
+       for (pos = lk_head->next; pos != lk_head && pos != NULL; pos = pos->next)
+               hdcp_info("Link: %d\n", pos->lk_data->id);
+       mutex_unlock(&lk_list->lk_mutex);
+}
+
+/**
+ * Find an entry from the Link list
+ * @id: Link handle to find link node
+ * @lk_list: link list contain the link node
+ */
+struct hdcp_link_node *hdcp_link_list_find(uint32_t id, struct hdcp_link_list *lk_list)
+{
+       struct hdcp_link_node *pos;
+       struct hdcp_link_node *lk_head;
+
+       if (!lk_list)
+               return NULL;
+
+       mutex_lock(&lk_list->lk_mutex);
+       lk_head = &lk_list->hdcp_link_head;
+       for (pos = lk_head->next; pos != lk_head && pos != NULL; pos = pos->next) {
+               if (pos->lk_data->id == id) {
+                       mutex_unlock(&lk_list->lk_mutex);
+                       return pos;
+               }
+       }
+       mutex_unlock(&lk_list->lk_mutex);
+       return NULL;
+}
+
+/**
+ * close all Links and remove all Link nodes
+ * @ss_list: session list to remove all
+ */
+void hdcp_link_list_destroy(struct hdcp_link_list *lk_list)
+{
+       struct hdcp_link_node *pos;
+       struct hdcp_link_node *lk_head;
+
+       if (!lk_list)
+               return;
+
+       mutex_lock(&lk_list->lk_mutex);
+       lk_head = &(lk_list->hdcp_link_head);
+       for (pos = lk_head->next; pos != lk_head && pos != NULL;) {
+               /* remove link node from the list*/
+               pos->prev->next = pos->next;
+               pos->next->prev = pos->prev;
+
+               /* remove link data */
+               /* TODO : remove all data */
+               if (pos)
+                       kfree(pos);
+               pos = lk_head->next;
+       }
+       lk_head->next = lk_head;
+       lk_head->prev = lk_head;
+       mutex_unlock(&lk_list->lk_mutex);
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-session.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-session.h
new file mode 100755 (executable)
index 0000000..02cbdac
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-tx-session.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __HDCP_TX_SESSION_H__
+#define __HDCP_TX_SESSION_H__
+
+#include "exynos-hdcp2-protocol-msg.h"
+
+#define WRAP_SKEY_EMPTY                0
+#define WRAP_SKEY_STORED       1
+
+#define HDCP_WITHOUT_STORED_KM 0
+#define HDCP_WITH_STORED_KM    1
+
+#define MAX_IP_LEN 15 /* IP address format is "xxx.xxx.xxx.xxx" */
+#define RECEIVER_ID_BYTE_LEN            (40 / 8)
+#define HDCP_PRIVATE_DATA_LEN           16
+#define HDCP_WRAPPED_HMAC_LEN           124
+#define REPEATER        1
+#define NO_RECEIVER     0
+
+
+/**
+ * HDCP Link state & data structure
+ */
+typedef enum hdcp_tx_hdcp_link_state {
+        LINK_ST_INIT = 0,
+        LINK_ST_H0_NO_RX_ATTATCHED,
+        LINK_ST_H1_TX_LOW_VALUE_CONTENT,
+        LINK_ST_A0_DETERMINE_RX_HDCP_CAP,
+        LINK_ST_A1_EXCHANGE_MASTER_KEY,
+        LINK_ST_A2_LOCALITY_CHECK,
+        LINK_ST_A3_EXCHANGE_SESSION_KEY,
+        LINK_ST_A4_TEST_REPEATER,
+        LINK_ST_A5_AUTHENTICATED,
+        LINK_ST_A6_WAIT_RECEIVER_ID_LIST,
+        LINK_ST_A7_VERIFY_RECEIVER_ID_LIST,
+        LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK,
+        LINK_ST_A9_CONTENT_STREAM_MGT,
+        LINK_ST_END
+} hdcp_tx_hdcp_link_state;
+
+struct hdcp_session_node {
+        struct hdcp_session_data *ss_data;
+        struct hdcp_session_node *next;
+        struct hdcp_session_node *prev;
+};
+
+struct hdcp_session_list {
+        struct hdcp_session_node hdcp_session_head;
+        struct mutex ss_mutex;
+};
+
+struct hdcp_timer {
+        struct timeval start;
+        struct timeval end;
+        uint32_t timeout; /* millisecond */
+        uint32_t elapsed_time; /* millisecond */
+};
+
+struct hdcp_link_data {
+        uint32_t id;
+        uint32_t state;
+        uint8_t stored_km;
+        uint32_t errno; /* error code */
+       uint32_t lk_type; /* link type */
+        struct hdcp_tx_ctx tx_ctx; /* Transmitter context data */
+        struct hdcp_rx_ctx rx_ctx; /* Receiver context data */
+        struct hdcp_timer timer; /* to check timeout */
+        struct hdcp_session_node *ss_ptr; /* session pointer link belong */
+};
+
+struct hdcp_link_node {
+        struct hdcp_link_data *lk_data;
+        struct hdcp_link_node *next;
+        struct hdcp_link_node *prev;
+};
+
+struct hdcp_link_list {
+        struct hdcp_link_node hdcp_link_head;
+        struct mutex lk_mutex;
+};
+
+/**
+ * HDCP Session status & data structure
+ */
+typedef enum hdcp_tx_ss_state {
+       SESS_ST_INIT = 0,
+       SESS_ST_LINK_SETUP,
+       SESS_ST_END
+} hdcp_tx_ss_state;
+
+struct hdcp_session_data {
+       uint32_t id;
+       hdcp_tx_ss_state state;
+       uint8_t wrap_skey_store;
+       uint8_t riv[HDCP_AKE_RTX_BYTE_LEN];
+       uint8_t wrap_skey[HDCP_AKE_WKEY_BYTE_LEN];
+       struct hdcp_link_list ln;
+};
+
+struct hdcp_session_data *hdcp_session_data_create(void);
+void hdcp_session_data_destroy(struct hdcp_session_data **ss_data);
+struct hdcp_link_data *hdcp_link_data_create(void);
+void hdcp_link_data_destroy(struct hdcp_link_data **lk_data);
+
+/* Session list APIs */
+void hdcp_session_list_init(struct hdcp_session_list *ss_list);
+void hdcp_session_list_add(struct hdcp_session_node *new_ent, struct hdcp_session_list *ss_list);
+void hdcp_session_list_del(struct hdcp_session_node *del_ent, struct hdcp_session_list *ss_list);
+void hdcp_session_list_print_all(struct hdcp_session_list *ss_list);
+void hdcp_session_list_destroy(struct hdcp_session_list *ss_list);
+struct hdcp_session_node *hdcp_session_list_find(uint32_t id, struct hdcp_session_list *ss_list);
+
+/* Link list APIs */
+void hdcp_link_list_init(struct hdcp_link_list *lk_list);
+void hdcp_link_list_add(struct hdcp_link_node *new_ent, struct hdcp_link_list *lk_list);
+void hdcp_link_list_del(struct hdcp_link_node *del_ent, struct hdcp_link_list *lk_list);
+void hdcp_link_list_print_all(struct hdcp_link_list *lk_list);
+void hdcp_link_list_destroy(struct hdcp_link_list *lk_list);
+struct hdcp_link_node *hdcp_link_list_find(uint32_t id, struct hdcp_link_list *lk_list);
+#endif
+
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.c
new file mode 100755 (executable)
index 0000000..981bdcc
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/smc.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+
+#include "exynos-hdcp2-teeif.h"
+#include "exynos-hdcp2.h"
+#include "exynos-hdcp2-log.h"
+
+extern void __inval_cache_range(const void *start, const void *end);
+
+static struct hci_ctx hctx = {
+       .msg = NULL,
+       .state = HCI_DISCONNECTED
+};
+
+int hdcp_tee_open(void)
+{
+       int ret = 0;
+       u64 phys_addr = 0;
+
+       if (hctx.state == HCI_CONNECTED) {
+               hdcp_info("HCI is already connected\n");
+               return 0;
+       }
+
+       /* Allocate WSM for HDCP commnad interface */
+       hctx.msg = (struct hci_message *)kzalloc(sizeof(struct hci_message), GFP_KERNEL);
+       if (!hctx.msg) {
+               hdcp_err("alloc wsm for HDCP SWd is failed\n");
+               return -ENOMEM;
+       }
+
+       /* send WSM address to SWd */
+       phys_addr = virt_to_phys((void *)hctx.msg);
+       ret = exynos_smc(SMC_HDCP_INIT, phys_addr, sizeof(struct hci_message), 0);
+       if (ret) {
+               hdcp_err("Fail to set up connection with SWd. ret(%d)\n", ret);
+               kfree(hctx.msg);
+               hctx.msg = NULL;
+               return -ECONNREFUSED;
+       }
+
+       hctx.state = HCI_CONNECTED;
+
+       return 0;
+}
+
+int hdcp_tee_close()
+{
+       int ret;
+
+       if (hctx.state == HCI_DISCONNECTED) {
+               hdcp_info("HCI is already disconnected\n");
+               return 0;
+       }
+
+       if (hctx.msg) {
+               kfree(hctx.msg);
+               hctx.msg = NULL;
+       }
+
+       /* send terminate command to SWd */
+       ret = exynos_smc(SMC_HDCP_TERMINATE, 0, 0, 0);
+       if (ret) {
+               hdcp_err("HDCP: Fail to set up connection with SWd. ret(%d)\n", ret);
+               return -EFAULT;
+       }
+
+       hctx.state = HCI_DISCONNECTED;
+
+       return 0;
+}
+
+int hdcp_tee_comm(struct hci_message *hci)
+{
+       int ret;
+
+       if (!hci)
+               return -EINVAL;
+
+       /**
+        * kernel & TEE does not share cache.
+        * So, cache should be flushed before sending data to TEE
+        * and invalidate after come back to kernel
+        */
+       __flush_dcache_area((void *)hci, sizeof(struct hci_message));
+       ret = exynos_smc(SMC_HDCP_PROT_MSG, 0, 0, 0);
+       __inval_cache_range((void *)hci, (void *)((uint8_t *)hci + sizeof(struct hci_message)));
+
+       if (ret) {
+               hdcp_info("SWd returned(%x)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int teei_gen_rtx(uint32_t lk_type,
+               uint8_t *rtx, size_t rtx_len,
+               uint8_t *caps, uint32_t caps_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GEN_RTX;
+       hci->genrtx.lk_type = lk_type;
+       hci->genrtx.len = rtx_len;
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* check returned message from SWD */
+       if (rtx && rtx_len)
+               memcpy(rtx, hci->genrtx.rtx, rtx_len);
+       if (caps && caps_len)
+               memcpy(caps, hci->genrtx.tx_caps, caps_len);
+
+       return ret;
+}
+
+int teei_get_txinfo(uint8_t *version, size_t version_len,
+               uint8_t *caps_mask, uint32_t caps_mask_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GET_TXINFO;
+
+       /* send command to swd */
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* check returned message from SWD */
+       memcpy(version, hci->gettxinfo.version, version_len);
+       memcpy(caps_mask, hci->gettxinfo.caps_mask, caps_mask_len);
+
+       return ret;
+}
+
+int teei_set_rxinfo(uint8_t *version, size_t version_len,
+               uint8_t *caps_mask, uint32_t caps_mask_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_SET_RXINFO;
+       memcpy(hci->setrxinfo.version, version, version_len);
+       memcpy(hci->setrxinfo.caps_mask, caps_mask, caps_mask_len);
+
+       /* send command to swd */
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       return ret;
+}
+
+int teei_verify_cert(uint8_t *cert, size_t cert_len,
+               uint8_t *rrx, size_t rrx_len,
+               uint8_t *rx_caps, size_t rx_caps_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_VERIFY_CERT;
+       hci->vfcert.len = cert_len;
+       memcpy(hci->vfcert.cert, cert, cert_len);
+       if (rrx && rrx_len)
+               memcpy(hci->vfcert.rrx, rrx, rrx_len);
+       if (rx_caps && rx_caps_len)
+               memcpy(hci->vfcert.rx_caps, rx_caps, rx_caps_len);
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* return verification result */
+       return ret;
+}
+
+int teei_generate_master_key(uint32_t lk_type, uint8_t *emkey, size_t emkey_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GEN_MKEY;
+       hci->genmkey.lk_type = lk_type;
+       hci->genmkey.emkey_len = emkey_len;
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* copy encrypted mkey & wrapped mkey to hdcp ctx */
+       memcpy(emkey, hci->genmkey.emkey, hci->genmkey.emkey_len);
+
+       /* check returned message from SWD */
+
+       return 0;
+}
+
+int teei_set_rrx(uint8_t *rrx, size_t rrx_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_SET_RRX;
+       memcpy(hci->setrrx.rrx, rrx, rrx_len);
+
+       /* send command to swd */
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       return ret;
+}
+
+int teei_compare_ake_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_COMPARE_AKE_HMAC;
+       hci->comphmac.rx_hmac_len = rx_hmac_len;
+       memcpy(hci->comphmac.rx_hmac, rx_hmac, rx_hmac_len);
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       return ret;
+}
+
+int teei_set_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_SET_PAIRING_INFO;
+       memcpy(hci->setpairing.ekh_mkey, ekh_mkey, ekh_mkey_len);
+
+       /* send command to swd */
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       return ret;
+}
+
+int teei_get_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len,
+                       uint8_t *m, size_t m_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GET_PAIRING_INFO;
+
+       /* send command to swd */
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       memcpy(ekh_mkey, hci->getpairing.ekh_mkey, ekh_mkey_len);
+       memcpy(m, hci->getpairing.m, m_len);
+
+       return ret;
+}
+
+int teei_gen_rn(uint8_t *out, size_t len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GEN_RN;
+       hci->genrn.len = len;
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* check returned message from SWD */
+
+       memcpy(out, hci->genrn.rn, len);
+
+       return ret;
+}
+
+int teei_gen_lc_hmac(uint32_t lk_type, uint8_t *lsb16_hmac)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GEN_LC_HMAC;
+       hci->genlchmac.lk_type = lk_type;
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* Send LSB 16bytes to Rx */
+       if (lsb16_hmac)
+               memcpy(lsb16_hmac, hci->genlchmac.lsb16_hmac, (HDCP_HMAC_SHA256_LEN / 2));
+
+       /* todo: check returned message from SWD */
+
+       return 0;
+}
+
+int teei_compare_lc_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_COMPARE_LC_HMAC;
+       hci->complchmac.rx_hmac_len = rx_hmac_len;
+       memcpy(hci->complchmac.rx_hmac, rx_hmac, rx_hmac_len);
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       return ret;
+}
+
+int teei_generate_riv(uint8_t *out, size_t len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GEN_RIV;
+       hci->genriv.len = len;
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       memcpy(out, hci->genriv.riv, len);
+
+       /* todo:  check returned message from SWD */
+
+       return ret;
+}
+
+int teei_generate_skey(uint32_t lk_type,
+               uint8_t *eskey, size_t eskey_len,
+               int share_skey)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_GEN_SKEY;
+       hci->genskey.lk_type = lk_type;
+       hci->genskey.eskey_len = eskey_len;
+       hci->genskey.share_skey = share_skey;
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       /* copy encrypted mkey & wrapped mkey to hdcp ctx */
+       memcpy(eskey, hci->genskey.eskey, hci->genskey.eskey_len);
+
+       /* todo: check returned message from SWD */
+
+       return 0;
+}
+
+int teei_encrypt_packet(uint8_t *input, size_t input_len,
+                       uint8_t *output, size_t output_len,
+                       uint8_t *str_ctr, size_t str_ctr_len,
+                       uint8_t *input_ctr, size_t input_ctr_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+       hci->cmd_id = HDCP_TEEI_ENC_PACKET;
+       hci->encpacket.input_len = input_len;
+       hci->encpacket.output_len = output_len;
+       hci->encpacket.input_addr = (uint64_t)input;
+       hci->encpacket.output_addr = (uint64_t)output;
+       memcpy(hci->encpacket.str_ctr, str_ctr, str_ctr_len);
+       memcpy(hci->encpacket.input_ctr, input_ctr, input_ctr_len);
+
+       hdcp_debug("teeif command(%x)\n", hci->cmd_id);
+       hdcp_debug("input(%llx), output(%llx)\n", hci->encpacket.input_addr, hci->encpacket.output_addr);
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       return 0;
+}
+
+int teei_set_rcvlist_info(uint8_t *rx_info,
+               uint8_t *seq_num_v,
+               uint8_t *v_prime,
+               uint8_t *rcvid_list,
+               uint8_t *v,
+               uint8_t *valid)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       hci->cmd_id = HDCP_TEEI_SET_RCV_ID_LIST;
+
+       memcpy(hci->setrcvlist.rcvid_lst, rcvid_list, HDCP_RP_RCVID_LIST_LEN);
+       memcpy(hci->setrcvlist.v_prime, v_prime, HDCP_RP_HMAC_V_LEN / 2);
+       /* Only used DP */
+       if (rx_info != NULL && seq_num_v != NULL) {
+               memcpy(hci->setrcvlist.rx_info, rx_info, HDCP_RP_RX_INFO_LEN);
+               memcpy(hci->setrcvlist.seq_num_v, seq_num_v, HDCP_RP_SEQ_NUM_V_LEN);
+       }
+
+
+       ret = hdcp_tee_comm(hci);
+       if (ret != 0) {
+               *valid = 1;
+               return ret;
+       }
+       memcpy(v, hci->setrcvlist.v, HDCP_RP_HMAC_V_LEN / 2);
+       *valid = 0;
+
+       return 0;
+}
+
+int teei_gen_stream_manage(uint16_t stream_num,
+               uint8_t *streamid,
+               uint8_t *seq_num_m,
+               uint8_t *k,
+               uint8_t *streamid_type)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+       /* todo: input check */
+
+       /* Update TCI buffer */
+
+       hci->cmd_id = HDCP_TEEI_GEN_STREAM_MANAGE;
+       hci->genstrminfo.stream_num = stream_num;
+       memcpy(hci->genstrminfo.streamid, streamid, sizeof(uint8_t) * stream_num);
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       memcpy(seq_num_m, hci->genstrminfo.seq_num_m, HDCP_RP_SEQ_NUM_M_LEN);
+       memcpy(k, hci->genstrminfo.k, HDCP_RP_K_LEN);
+       memcpy(streamid_type, hci->genstrminfo.streamid_type, HDCP_RP_STREAMID_TYPE_LEN);
+
+       /* check returned message from SWD */
+
+       /* return verification result */
+       return ret;
+}
+
+int teei_verify_m_prime(uint8_t *m_prime, uint8_t *input, size_t input_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       hci->cmd_id = HDCP_TEEI_VERIFY_M_PRIME;
+       memcpy(hci->verifymprime.m_prime, m_prime, HDCP_RP_HMAC_M_LEN);
+       if (input && input_len < sizeof(hci->verifymprime.strmsg)) {
+               memcpy(hci->verifymprime.strmsg, input, input_len);
+               hci->verifymprime.str_len = input_len;
+       }
+
+       ret = hdcp_tee_comm(hci);
+
+       return ret;
+}
+
+int teei_wrapped_key(uint8_t *key, uint32_t wrapped, uint32_t key_len)
+{
+       int ret = 0;
+       struct hci_message *hci = hctx.msg;
+
+       hci->cmd_id = HDCP_TEEI_WRAP_KEY;
+
+       if (key_len > (sizeof(hci->wrap_key.enc_key) - HDCP_WRAP_AUTH_TAG)) {
+               hdcp_err("(un)wraaping key size is wrong. 0x%x\n", key_len);
+               return HDCP_ERROR_WRONG_SIZE;
+       }
+
+       if (key_len < HDCP_WRAP_AUTH_TAG || key_len > HDCP_WRAP_MAX_SIZE - HDCP_WRAP_AUTH_TAG)
+               return HDCP_ERROR_WRONG_SIZE;
+
+       if (wrapped == UNWRAP)
+               memcpy(hci->wrap_key.enc_key, key, key_len + HDCP_WRAP_AUTH_TAG);
+       else
+               memcpy(hci->wrap_key.key, key, key_len);
+
+       hci->wrap_key.wrapped = wrapped;
+       hci->wrap_key.key_len = key_len;
+
+       if ((ret = hdcp_tee_comm(hci)) < 0)
+               return ret;
+
+       if (hci->wrap_key.wrapped == WRAP) {
+               memcpy(key, hci->wrap_key.enc_key, key_len + HDCP_WRAP_AUTH_TAG);
+       }
+
+       return ret;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.h
new file mode 100755 (executable)
index 0000000..cba2187
--- /dev/null
@@ -0,0 +1,332 @@
+/* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __EXYNOS_HDCP2_TEEIF_H__
+#define __EXYNOS_HDCP2_TEEIF_H__
+
+/* SMC list for HDCP functions */
+#define SMC_HDCP_INIT                  ((unsigned int)0x82004010)
+#define SMC_HDCP_TERMINATE             ((unsigned int)0x82004011)
+#define SMC_HDCP_PROT_MSG              ((unsigned int)0x82004012)
+#define SMC_CHECK_STREAM_TYPE_FALG     ((unsigned int)0x82004022)
+
+#define SMC_DRM_HDCP_AUTH_INFO         ((unsigned int)0x82002140)
+
+/**
+ * HDCP TEE service commands
+ */
+enum {
+       HDCP_TEEI_GEN_RTX = 0x0,
+       HDCP_TEEI_VERIFY_CERT,
+       HDCP_TEEI_GEN_MKEY,
+       HDCP_TEEI_GEN_AKE_HMAC,
+       HDCP_TEEI_COMPARE_AKE_HMAC,
+       HDCP_TEEI_GEN_RN,
+       HDCP_TEEI_GEN_LC_HMAC,
+       HDCP_TEEI_COMPARE_LC_HMAC,
+       HDCP_TEEI_GEN_RIV,
+       HDCP_TEEI_GEN_SKEY,
+       HDCP_TEEI_ENC_PACKET,
+       HDCP_TEEI_GET_TXINFO,
+       HDCP_TEEI_SET_RXINFO,
+       HDCP_TEEI_SET_RRX,
+       HDCP_TEEI_SET_PAIRING_INFO,
+       HDCP_TEEI_GET_PAIRING_INFO,
+       HDCP_TEEI_SET_RCV_ID_LIST,
+       HDCP_TEEI_GEN_STREAM_MANAGE,
+       HDCP_TEEI_VERIFY_M_PRIME,
+       HDCP_TEEI_WRAP_KEY,
+       HDCP_TEEI_MSG_END
+};
+
+#define HCI_DISCONNECTED       0
+#define HCI_CONNECTED          1
+
+#define HDCP_WSM_SIZE  (1024)
+#define AKE_INFO_SIZE  (128)
+
+#define HDCP_RX_MODULUS_LEN            (1024 / 8)
+#define HDCP_RX_PUB_EXP_LEN            (24 / 8)
+#define HDCP_AKE_ENCKEY_BYTE_LEN       (1024 / 8)
+#define HDCP_AKE_MKEY_BYTE_LEN         (128 / 8)
+#define HDCP_AKE_M_BYTE_LEN            HDCP_AKE_MKEY_BYTE_LEN
+#define HDCP_AKE_EKH_MKEY_BYTE_LEN     HDCP_AKE_MKEY_BYTE_LEN
+#define        HDCP_AKE_RTX_BYTE_LEN           (64 / 8)
+#define HDCP_AKE_HMAC_APPEND           (6)
+#define HDCP_STR_CTR_LEN               (4)
+#define HDCP_INPUT_CTR_LEN             (8)
+#define RECEIVER_ID_BYTE_LEN           (40 / 8)
+#define HDCP_RRX_BYTE_LEN              (64 / 8)
+#define HDCP_RTX_BYTE_LEN              HDCP_RRX_BYTE_LEN
+#define HDCP_HMAC_SHA256_LEN           (256 / 8)
+#define HDCP_WRAP_APPEND_LEN           (92)
+#define HDCP_RX_CERT_LEN               (522)
+#define HDCP_AKE_WRAPPED_HMAC_LEN      (32)
+#define HDCP_AKE_WKEY_BYTE_LEN         (32)
+#define HDCP_SKE_SKEY_LEN              (128 / 8)
+#define HDCP_SKE_WSKEY_BYTE_LEN                (32)
+#define MKEY_LEN                       (16)
+#define CERT_LEN                       (128)
+#define HMAC_LEN                       (32)
+#define HDCP_RX_CERT_INFO_LEN          (138)
+#define HDCP_CERT_SIGLEN               (384)
+#define HDCP_RN_BYTE_LEN               (64 / 8)
+#define HDCP_RIV_BYTE_LEN              (64 / 8)
+#define HDCP_CAPS_BYTE_LEN             (3)
+#define HDCP_VERSION_LEN               (1)
+#define HDCP_CAPABILITY_MASK_LEN       (2)
+#define HDCP_RP_HMAC_V_LEN             HDCP_HMAC_SHA256_LEN
+#define HDCP_RP_HMAC_M_LEN             HDCP_HMAC_SHA256_LEN
+#define HDCP_RP_RX_INFO_LEN            (2)
+#define HDCP_RP_SEQ_NUM_V_LEN          (3)
+#define HDCP_RCV_ID_LEN                 (5)
+#define HDCP_RCV_DEVS_COUNT_MAX         (32)
+#define HDCP_RP_RCVID_LIST_LEN         (HDCP_RCV_ID_LEN * HDCP_RCV_DEVS_COUNT_MAX)
+#define HDCP_RP_SEQ_NUM_M_LEN          (3)
+#define HDCP_RP_K_LEN                  (2)
+#define HDCP_RP_MAX_STREAMID_NUM       (63)
+#define HDCP_RP_TYPE_LEN               (1)
+#define HDCP_RP_STREAMID_LEN           (1)
+#define HDCP_RP_STREAM_MSG_MAX_LEN     (32 * 8)
+#define HDCP_RP_STREAMID_TYPE_LEN      (HDCP_RP_STREAMID_LEN + HDCP_RP_TYPE_LEN)
+#define HDCP_RP_MAX_STREAMID_TYPE_LEN  (HDCP_RP_STREAMID_TYPE_LEN * HDCP_RP_MAX_STREAMID_NUM)
+#define HDCP_STATIC_KEY                        (16)
+#define HDCP_WRAP_KEY                  (16)
+#define HDCP_WRAP_MAX_SIZE             (128)
+#define HDCP_WRAP_AUTH_TAG             (16)
+#define HDCP_RX_CAPS_LEN               (1)
+
+/* TEEI error code */
+enum hdcp_rv_t  {
+       HDCP_OK                         = 0x0000,
+       E_HDCP_PRO_INVALID_RCV_ID       = 0x3015,
+};
+
+enum kw_mode {
+        WRAP,
+        UNWRAP,
+};
+
+typedef struct {
+       uint32_t id;
+       uint32_t len;
+       uint32_t lk_type;
+       uint8_t rtx[HDCP_RTX_BYTE_LEN];
+       uint8_t tx_caps[HDCP_CAPS_BYTE_LEN];
+} hci_genrtx_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t version[HDCP_VERSION_LEN];
+       uint8_t caps_mask[HDCP_CAPABILITY_MASK_LEN];
+} hci_gettxinfo_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t len;
+       uint8_t cert[HDCP_RX_CERT_LEN];
+       uint8_t rrx[HDCP_RRX_BYTE_LEN];
+       uint8_t rx_caps[HDCP_CAPS_BYTE_LEN];
+} hci_verifycert_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t version[HDCP_VERSION_LEN];
+       uint8_t caps_mask[HDCP_CAPABILITY_MASK_LEN];
+} hci_setrxinfo_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t lk_type;
+       uint32_t emkey_len;
+       uint8_t emkey[HDCP_AKE_ENCKEY_BYTE_LEN];
+} hci_genmkey_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t rrx[HDCP_RRX_BYTE_LEN];
+} hci_setrrx_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t rx_hmac_len;
+       uint8_t rx_hmac[HDCP_HMAC_SHA256_LEN];
+} hci_comphmac_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t ekh_mkey[HDCP_AKE_EKH_MKEY_BYTE_LEN];
+} hci_setpairing_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t ekh_mkey[HDCP_AKE_EKH_MKEY_BYTE_LEN];
+       uint8_t m[HDCP_AKE_M_BYTE_LEN];
+} hci_getpairing_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t len;
+       uint8_t rn[HDCP_RN_BYTE_LEN];
+} hci_genrn_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t lk_type;
+       uint8_t lsb16_hmac[16];
+} hci_genlchmac_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t rx_hmac_len;
+       uint8_t rx_hmac[HDCP_HMAC_SHA256_LEN];
+} hci_complchmac_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t len;
+       uint8_t riv[HDCP_RIV_BYTE_LEN];
+} hci_genriv_t;
+
+typedef struct {
+       uint32_t id;
+       uint32_t lk_type;
+       uint8_t eskey[HDCP_SKE_SKEY_LEN];
+       uint32_t eskey_len;
+       int share_skey;
+} hci_genskey_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t str_ctr[HDCP_STR_CTR_LEN];
+       uint8_t input_ctr[HDCP_INPUT_CTR_LEN];
+       uint64_t input_addr;
+       uint32_t input_len;
+       uint64_t output_addr;
+       uint32_t output_len;
+} hci_encpacket_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t rx_info[HDCP_RP_RX_INFO_LEN];
+       uint8_t seq_num_v[HDCP_RP_SEQ_NUM_V_LEN];
+       uint8_t v_prime[HDCP_RP_HMAC_V_LEN / 2];
+       uint8_t rcvid_lst[HDCP_RP_RCVID_LIST_LEN];
+       uint8_t v[HDCP_RP_HMAC_V_LEN / 2];
+} hci_setrcvlist_t;
+
+typedef struct {
+       uint32_t id;
+       uint16_t stream_num;
+       uint8_t streamid[HDCP_RP_MAX_STREAMID_NUM];
+       uint8_t seq_num_m[HDCP_RP_SEQ_NUM_M_LEN];
+       uint8_t k[HDCP_RP_K_LEN];
+       uint8_t streamid_type[HDCP_RP_STREAMID_TYPE_LEN];
+} hci_genstreaminfo_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t m_prime[HDCP_RP_HMAC_M_LEN];
+       uint8_t strmsg[HDCP_RP_STREAM_MSG_MAX_LEN];
+       uint32_t str_len;
+} hci_verifymprime_t;
+
+typedef struct {
+       uint32_t id;
+       uint8_t key[HDCP_WRAP_KEY];
+       uint8_t enc_key[HDCP_WRAP_MAX_SIZE];
+       uint32_t wrapped;
+       uint32_t key_len;
+} hci_wrap_key_t;
+
+/* todo: define WSM message format for AKE */
+struct hci_message {
+       union {
+               uint32_t cmd_id;
+               hci_genrtx_t genrtx;
+               hci_gettxinfo_t gettxinfo;
+               hci_verifycert_t vfcert;
+               hci_setrxinfo_t setrxinfo;
+               hci_setrrx_t setrrx;
+               hci_genmkey_t genmkey;
+               hci_comphmac_t comphmac;
+               hci_setpairing_t setpairing;
+               hci_getpairing_t getpairing;
+               hci_genrn_t genrn;
+               hci_genlchmac_t genlchmac;
+               hci_complchmac_t complchmac;
+               hci_genriv_t genriv;
+               hci_genskey_t genskey;
+               hci_encpacket_t encpacket;
+               hci_setrcvlist_t setrcvlist;
+               hci_genstreaminfo_t genstrminfo;
+               hci_verifymprime_t verifymprime;
+               hci_wrap_key_t wrap_key;
+               uint8_t data[HDCP_WSM_SIZE];
+       };
+};
+
+struct hci_ctx {
+       struct hci_message *msg;
+       uint8_t state;
+};
+
+int hdcp_tee_open(void);
+int hdcp_tee_close(void);
+int hdcp_tee_comm(struct hci_message *hci);
+
+/* HDCP TEE interfaces */
+int teei_gen_rtx(uint32_t lk_type,
+               uint8_t *rtx,
+               size_t rtx_len,
+               uint8_t *caps,
+               uint32_t caps_len);
+int teei_get_txinfo(uint8_t *version, size_t version_len,
+               uint8_t *caps_mask, uint32_t caps_mask_len);
+int teei_verify_cert(uint8_t *cert, size_t cert_len,
+               uint8_t *rrx, size_t rrx_len,
+               uint8_t *rx_caps, size_t rx_caps_len);
+int teei_set_rxinfo(uint8_t *version, size_t version_len,
+               uint8_t *caps_mask, uint32_t caps_mask_len);
+int teei_set_rrx(uint8_t *rrx, size_t rrx_len);
+int teei_generate_master_key(uint32_t lk_type,
+                       uint8_t *emkey, size_t emkey_len);
+int teei_compare_ake_hmac(uint8_t *hmac, size_t hamc_len);
+int teei_set_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len);
+int teei_get_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len,
+                       uint8_t *m, size_t m_len);
+
+/* LC interface */
+int teei_gen_rn(uint8_t *out, size_t len);
+int teei_gen_lc_hmac(uint32_t lk_type, uint8_t *lsb128_hmac);
+int teei_compare_lc_hmac(uint8_t *rx_hmac, size_t rx_hmac_len);
+int teei_generate_riv(uint8_t *out, size_t len);
+int teei_generate_skey(uint32_t lk_type,
+               uint8_t *eskey, size_t eskey_len,
+               int share_skey);
+int teei_encrypt_packet(uint8_t *input, size_t input_len,
+                       uint8_t *output, size_t output_len,
+                       uint8_t *str_ctr, size_t str_ctr_len,
+                       uint8_t *input_ctr, size_t input_ctr_len);
+
+/* Repeater Auth interface */
+int teei_set_rcvlist_info(uint8_t *rx_info,
+               uint8_t *seq_num_v,
+               uint8_t *v_prime,
+               uint8_t *rcvid_list,
+               uint8_t *v,
+               uint8_t *valid);
+int teei_gen_stream_manage(uint16_t stream_num,
+               uint8_t *streamid,
+               uint8_t *seq_num_m,
+               uint8_t *k,
+               uint8_t *streamid_type);
+int teei_verify_m_prime(uint8_t *m_prime, uint8_t *input, size_t input_len);
+int teei_wrapped_key(uint8_t *key_str, uint32_t wrraped, uint32_t key_len);
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-testvector.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-testvector.h
new file mode 100644 (file)
index 0000000..fd738be
--- /dev/null
@@ -0,0 +1,909 @@
+/* soc/samsung/exynos-hdcp/exynos-hdcp2-testvector.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __EXYNOS_HDCP2_TEST_VECTOR_H__
+#define __EXYNOS_HDCP2_TEST_VECTOR_H__
+
+#include "exynos-hdcp2-config.h"
+#include "exynos-hdcp2-protocol-msg.h"
+
+#ifdef TEST_VECTOR_1
+unsigned char cert[522] = {
+       0x74, 0x5b, 0xb8, 0xbd, 0x04, 0xbc, 0x83, 0xc7,
+       0x95, 0x78, 0xf9, 0x0c, 0x91, 0x4b, 0x89, 0x38,
+       0x05, 0x5a, 0xa4, 0xac, 0x1f, 0xa8, 0x03, 0x93,
+       0x82, 0x79, 0x75, 0xaf, 0x66, 0x22, 0xde, 0x43,
+       0x80, 0x8d, 0xcd, 0x5d, 0x90, 0xb8, 0x3c, 0xb3,
+       0xd8, 0x9e, 0xb0, 0x0d, 0x09, 0x44, 0xf4, 0x3f,
+       0x5f, 0xab, 0xb9, 0xc4, 0xc9, 0x96, 0xef, 0x78,
+       0xb5, 0x8f, 0x69, 0x77, 0xb4, 0x7d, 0x08, 0x14,
+       0x9c, 0x81, 0xa0, 0x8f, 0x04, 0x1f, 0xa0, 0x88,
+       0xe1, 0x20, 0xc7, 0x34, 0x4a, 0x49, 0x35, 0x65,
+       0x99, 0xcf, 0x53, 0x19, 0xf0, 0xc6, 0x81, 0x76,
+       0x05, 0x5c, 0xb9, 0xde, 0xdd, 0xab, 0x3d, 0xb0,
+       0x92, 0xa1, 0x23, 0x4f, 0x0c, 0x71, 0x30, 0x42,
+       0x78, 0xf6, 0x55, 0xae, 0xbd, 0x36, 0x25, 0x8e,
+       0x25, 0x0d, 0x4e, 0x5e, 0x8e, 0x77, 0x6a, 0x60,
+       0xe3, 0xc1, 0xe9, 0xee, 0xcd, 0x2b, 0x9e, 0x18,
+       0x63, 0x97, 0xd4, 0xe6, 0x75, 0x01, 0x00, 0x01,
+       0x00, 0x00, 0x1d, 0x0a, 0x61, 0xea, 0xab, 0xf8,
+       0xa8, 0x2b, 0x02, 0x69, 0xa1, 0x34, 0xfd, 0x91,
+       0xac, 0x2b, 0xf2, 0x8f, 0x34, 0x8b, 0xd4, 0x84,
+       0xfa, 0x62, 0xbc, 0x01, 0x4a, 0x4a, 0xa2, 0xb2,
+       0x14, 0xbf, 0xb5, 0xf4, 0xdf, 0xac, 0x80, 0x93,
+       0x0d, 0x13, 0xec, 0x9c, 0xe5, 0xd8, 0x34, 0x70,
+       0x51, 0x9a, 0x66, 0x80, 0xeb, 0xbe, 0xcc, 0x7e,
+       0x45, 0xf0, 0xe6, 0x39, 0x63, 0x84, 0xc9, 0xb9,
+       0x8e, 0x8c, 0xaf, 0x9c, 0xa9, 0xd4, 0x0e, 0xeb,
+       0x9a, 0x57, 0x2a, 0x17, 0x41, 0xca, 0x97, 0xf3,
+       0x19, 0x96, 0xb5, 0x5d, 0x0f, 0x30, 0xa3, 0x84,
+       0xe5, 0x73, 0xa2, 0xed, 0x05, 0x69, 0x7a, 0x22,
+       0xce, 0x84, 0x1f, 0x3e, 0x39, 0x9e, 0x28, 0x76,
+       0xc9, 0xbc, 0x89, 0x5b, 0x70, 0xb1, 0x7b, 0xf4,
+       0xed, 0xb6, 0x74, 0x12, 0xab, 0x48, 0x29, 0x64,
+       0xce, 0x6c, 0x60, 0x04, 0xeb, 0xa9, 0x7a, 0xa2,
+       0x15, 0xa6, 0x58, 0x9a, 0xad, 0x32, 0xc7, 0x53,
+       0x39, 0xe5, 0xfe, 0xf0, 0x37, 0xa7, 0xa0, 0xc5,
+       0xff, 0xec, 0xd9, 0xb0, 0x05, 0xbb, 0x25, 0x13,
+       0xa0, 0xa4, 0xc7, 0x0b, 0x2a, 0x5d, 0xc6, 0x8f,
+       0x51, 0x11, 0xcb, 0x36, 0xed, 0x5c, 0x17, 0x7e,
+       0x22, 0x20, 0xc3, 0xeb, 0x40, 0x8c, 0x67, 0xbb,
+       0x1c, 0xd2, 0x47, 0xb0, 0xe0, 0xbd, 0xe7, 0x4c,
+       0xcd, 0x5d, 0xd5, 0x23, 0x12, 0xf8, 0x3b, 0x1d,
+       0x91, 0x3b, 0xf3, 0xc7, 0x60, 0xea, 0x90, 0x24,
+       0x48, 0xe5, 0x92, 0x21, 0x6c, 0xf6, 0xd9, 0x5e,
+       0x76, 0x8d, 0x2b, 0x86, 0xa6, 0x7c, 0x16, 0xae,
+       0xa8, 0x36, 0x08, 0xa0, 0x37, 0x14, 0x1a, 0xd7,
+       0x03, 0xe1, 0x40, 0x31, 0xca, 0x6c, 0x95, 0xe0,
+       0x10, 0xb0, 0x43, 0xcf, 0xb7, 0xe0, 0x30, 0x05,
+       0xb9, 0xac, 0xb7, 0x08, 0x68, 0xcd, 0x7e, 0x11,
+       0x47, 0x2a, 0x03, 0x3b, 0xeb, 0x74, 0xc8, 0x19,
+       0x62, 0x8b, 0x2f, 0x11, 0x91, 0xb6, 0x06, 0x4f,
+       0xe0, 0x2a, 0x44, 0x20, 0x43, 0x29, 0x13, 0x1f,
+       0xdd, 0xd0, 0x4a, 0x11, 0x6c, 0x0e, 0x83, 0xbf,
+       0x22, 0x62, 0x3b, 0xeb, 0xec, 0xd7, 0x76, 0x28,
+       0xba, 0x64, 0x88, 0x42, 0xc8, 0x73, 0xa7, 0x9e,
+       0x4a, 0x69, 0x3a, 0xb2, 0x0c, 0x4b, 0x3a, 0xd9,
+       0x50, 0xdb, 0x7c, 0x51, 0xee, 0x15, 0xe0, 0x6b,
+       0x2c, 0x63, 0xa6, 0x91, 0x57, 0xdd, 0xbf, 0x17,
+       0x47, 0x23, 0xad, 0x15, 0xcb, 0xb9, 0x91, 0x18,
+       0x0b, 0x51, 0x8f, 0xf9, 0x1c, 0x51, 0x67, 0xc1,
+       0x0b, 0x78, 0xf5, 0xd9, 0x55, 0xdc, 0x48, 0xe4,
+       0xc0, 0x83, 0xa5, 0xdf, 0x75, 0xe2, 0xdc, 0x88,
+       0xd2, 0xc6, 0xdd, 0xdf, 0x1f, 0x37, 0x90, 0x35,
+       0xf6, 0xfd, 0xda, 0xe0, 0x04, 0x32, 0x69, 0xc1,
+       0xaf, 0xd9, 0xf9, 0x11, 0xc5, 0xaa, 0x74, 0x58,
+       0x32, 0x1c, 0x71, 0xaa, 0xa7, 0x14, 0xfb, 0x23,
+       0x17, 0x22};
+
+/* HDCP2.2 R1 */
+unsigned char cert_v22[522] = {
+       0x74, 0x5b, 0xb8, 0xbd, 0x04, 0xaf, 0xb5, 0xc5,
+       0xc6, 0x7b, 0xc5, 0x3a, 0x34, 0x90, 0xa9, 0x54,
+       0xc0, 0x8f, 0xb7, 0xeb, 0xa1, 0x54, 0xd2, 0x4f,
+       0x22, 0xde, 0x83, 0xf5, 0x03, 0xa6, 0xc6, 0x68,
+       0x46, 0x9b, 0xc0, 0xb8, 0xc8, 0x6c, 0xdb, 0x26,
+       0xf9, 0x3c, 0x49, 0x2f, 0x02, 0xe1, 0x71, 0xdf,
+       0x4e, 0xf3, 0x0e, 0xc8, 0xbf, 0x22, 0x9d, 0x04,
+       0xcf, 0xbf, 0xa9, 0x0d, 0xff, 0x68, 0xab, 0x05,
+       0x6f, 0x1f, 0x12, 0x8a, 0x68, 0x62, 0xeb, 0xfe,
+       0xc9, 0xea, 0x9f, 0xa7, 0xfb, 0x8c, 0xba, 0xb1,
+       0xbd, 0x65, 0xac, 0x35, 0x9c, 0xa0, 0x33, 0xb1,
+       0xdd, 0xa6, 0x05, 0x36, 0xaf, 0x00, 0xa2, 0x7f,
+       0xbc, 0x07, 0xb2, 0xdd, 0xb5, 0xcc, 0x57, 0x5c,
+       0xdc, 0xc0, 0x95, 0x50, 0xe5, 0xff, 0x1f, 0x20,
+       0xdb, 0x59, 0x46, 0xfa, 0x47, 0xc4, 0xed, 0x12,
+       0x2e, 0x9e, 0x22, 0xbd, 0x95, 0xa9, 0x85, 0x59,
+       0xa1, 0x59, 0x3c, 0xc7, 0x83, 0x01, 0x00, 0x01,
+       0x10, 0x00, 0x0b, 0xa3, 0x73, 0x77, 0xdd, 0x03,
+       0x18, 0x03, 0x8a, 0x91, 0x63, 0x29, 0x1e, 0xa2,
+       0x95, 0x74, 0x42, 0x90, 0x78, 0xd0, 0x67, 0x25,
+       0xb6, 0x32, 0x2f, 0xcc, 0x23, 0x2b, 0xad, 0x21,
+       0x39, 0x3d, 0x14, 0xba, 0x37, 0xa3, 0x65, 0x14,
+       0x6b, 0x9c, 0xcf, 0x61, 0x20, 0x44, 0xa1, 0x07,
+       0xbb, 0xcf, 0xc3, 0x4e, 0x95, 0x5b, 0x10, 0xcf,
+       0xc7, 0x6f, 0xf1, 0xc3, 0x53, 0x7c, 0x63, 0xa1,
+       0x8c, 0xb2, 0xe8, 0xab, 0x2e, 0x96, 0x97, 0xc3,
+       0x83, 0x99, 0x70, 0xd3, 0xdc, 0x21, 0x41, 0xf6,
+       0x0a, 0xd1, 0x1a, 0xee, 0xf4, 0xcc, 0xeb, 0xfb,
+       0xa6, 0xaa, 0xb6, 0x9a, 0xaf, 0x1d, 0x16, 0x5e,
+       0xe2, 0x83, 0xa0, 0x4a, 0x41, 0xf6, 0x7b, 0x07,
+       0xbf, 0x47, 0x85, 0x28, 0x6c, 0xa0, 0x77, 0xa6,
+       0xa3, 0xd7, 0x85, 0xa5, 0xc4, 0xa7, 0xe7, 0x6e,
+       0xb5, 0x1f, 0x40, 0x72, 0x97, 0xfe, 0xc4, 0x81,
+       0x23, 0xa0, 0xc2, 0x90, 0xb3, 0x49, 0x24, 0xf5,
+       0xb7, 0x90, 0x2c, 0xbf, 0xfe, 0x04, 0x2e, 0x00,
+       0xa9, 0x5f, 0x86, 0x04, 0xca, 0xc5, 0x3a, 0xcc,
+       0x26, 0xd9, 0x39, 0x7e, 0xa9, 0x2d, 0x28, 0x6d,
+       0xc0, 0xcc, 0x6e, 0x81, 0x9f, 0xb9, 0xb7, 0x11,
+       0x33, 0x32, 0x23, 0x47, 0x98, 0x43, 0x0d, 0xa5,
+       0x1c, 0x59, 0xf3, 0xcd, 0xd2, 0x4a, 0xb7, 0x3e,
+       0x69, 0xd9, 0x21, 0x53, 0x9a, 0xf2, 0x6e, 0x77,
+       0x62, 0xae, 0x50, 0xda, 0x85, 0xc6, 0xaa, 0xc4,
+       0xb5, 0x1c, 0xcd, 0xa8, 0xa5, 0xdd, 0x6e, 0x62,
+       0x73, 0xff, 0x5f, 0x7b, 0xd7, 0x3c, 0x17, 0xba,
+       0x47, 0x0c, 0x89, 0x0e, 0x62, 0x79, 0x43, 0x94,
+       0xaa, 0xa8, 0x47, 0xf4, 0x4c, 0x38, 0x89, 0xa8,
+       0x81, 0xad, 0x23, 0x13, 0x27, 0x0c, 0x17, 0xcf,
+       0x3d, 0x83, 0x84, 0x57, 0x36, 0xe7, 0x22, 0x26,
+       0x2e, 0x76, 0xfd, 0x56, 0x80, 0x83, 0xf6, 0x70,
+       0xd4, 0x5c, 0x91, 0x48, 0x84, 0x7b, 0x18, 0xdb,
+       0x0e, 0x15, 0x3b, 0x49, 0x26, 0x23, 0xe6, 0xa3,
+       0xe2, 0xc6, 0x3a, 0x23, 0x57, 0x66, 0xb0, 0x72,
+       0xb8, 0x12, 0x17, 0x4f, 0x86, 0xfe, 0x48, 0x0d,
+       0x53, 0xea, 0xfe, 0x31, 0x48, 0x7d, 0x86, 0xde,
+       0xeb, 0x82, 0x86, 0x1e, 0x62, 0x03, 0x98, 0x59,
+       0x00, 0x37, 0xeb, 0x61, 0xe9, 0xf9, 0x7a, 0x40,
+       0x78, 0x1c, 0xba, 0xbc, 0x0b, 0x88, 0xfb, 0xfd,
+       0x9d, 0xd5, 0x01, 0x11, 0x94, 0xe0, 0x35, 0xbe,
+       0x33, 0xe8, 0xe5, 0x36, 0xfb, 0x9c, 0x45, 0xcb,
+       0x75, 0xaf, 0xd6, 0x35, 0xff, 0x78, 0x92, 0x7f,
+       0xa1, 0x7c, 0xa8, 0xfc, 0xb7, 0xf7, 0xa8, 0x52,
+       0xa9, 0xc6, 0x84, 0x72, 0x3d, 0x1c, 0xc9, 0xdf,
+       0x35, 0xc6, 0xe6, 0x00, 0xe1, 0x48, 0x72, 0xce,
+       0x83, 0x1b, 0xcc, 0xf8, 0x33, 0x2d, 0x4f, 0x98,
+       0x75, 0x00, 0x3c, 0x41, 0xdf, 0x7a, 0xed, 0x38,
+       0x53, 0xb1};
+#else
+unsigned char cert[522] = {
+       0x8b, 0xa4, 0x47, 0x42, 0xfb, 0xc9, 0x1b, 0x82,
+       0xe2, 0x76, 0x7f, 0x90, 0x4f, 0xe9, 0x12, 0x33,
+       0x7c, 0x21, 0x1f, 0x7b, 0x25, 0xda, 0x76, 0xde,
+       0xae, 0x59, 0x70, 0xf7, 0xc2, 0xe7, 0xe0, 0x4a,
+       0xcf, 0xbd, 0x5b, 0xba, 0x1c, 0x36, 0x4e, 0xe3,
+       0x78, 0x4c, 0x92, 0x6a, 0x3c, 0xd8, 0xc1, 0xe9,
+       0x51, 0xa9, 0x35, 0xeb, 0xd8, 0xe8, 0xd5, 0x3e,
+       0x3b, 0x1d, 0x00, 0xc1, 0x16, 0x16, 0xd0, 0x58,
+       0xeb, 0x2a, 0x4b, 0xa0, 0x76, 0x9c, 0xd0, 0xe4,
+       0xb2, 0x23, 0xdc, 0xaa, 0x37, 0x07, 0xe5, 0x85,
+       0x1a, 0xaa, 0x13, 0x55, 0x01, 0x4e, 0xed, 0x88,
+       0xca, 0x3f, 0xfb, 0xc5, 0x58, 0x46, 0x91, 0xec,
+       0x35, 0x99, 0x08, 0x1c, 0xa1, 0x22, 0x64, 0xe8,
+       0x3c, 0x2e, 0x70, 0xdf, 0xa9, 0x10, 0x14, 0x81,
+       0x46, 0xa2, 0x38, 0x08, 0xef, 0x1b, 0xd2, 0x46,
+       0xee, 0x38, 0x0d, 0x6d, 0x92, 0xd3, 0xf2, 0x02,
+       0xe7, 0xe4, 0x29, 0xad, 0x0d, 0x01, 0x00, 0x01,
+       0x00, 0x00, 0x91, 0x18, 0x81, 0xa5, 0xcd, 0xab,
+       0x78, 0x50, 0xad, 0x1d, 0x3b, 0x77, 0xbe, 0x51,
+       0x32, 0x9f, 0x04, 0xe6, 0x3e, 0xf7, 0x01, 0x39,
+       0xf2, 0x59, 0x98, 0x75, 0x9d, 0x29, 0x12, 0x33,
+       0x39, 0xb4, 0x80, 0x91, 0x9d, 0x6a, 0xff, 0x0d,
+       0x5c, 0x59, 0x22, 0x43, 0x77, 0xfc, 0xed, 0xc2,
+       0x40, 0x9d, 0xe2, 0xd1, 0x4b, 0xff, 0x02, 0x78,
+       0x36, 0xd3, 0xad, 0xcb, 0xa6, 0xd3, 0xd3, 0x9d,
+       0xcc, 0xff, 0xcb, 0x3c, 0xa3, 0xcb, 0xfd, 0xdf,
+       0xcf, 0xe2, 0x85, 0xa8, 0xbd, 0xa2, 0xf6, 0x60,
+       0x06, 0xb2, 0x9b, 0x53, 0xc4, 0xd6, 0x22, 0xbd,
+       0x65, 0x3c, 0x6f, 0x40, 0x01, 0x7c, 0x2c, 0x78,
+       0x89, 0x31, 0x70, 0x47, 0x56, 0x88, 0xf5, 0x56,
+       0x33, 0xf2, 0x0a, 0x91, 0x27, 0xb1, 0x68, 0x5f,
+       0x84, 0x98, 0x1d, 0x37, 0xbd, 0x69, 0x11, 0x6d,
+       0x60, 0xca, 0x01, 0x44, 0xbe, 0xfa, 0x92, 0x1f,
+       0xec, 0x15, 0xbe, 0x37, 0x68, 0xd1, 0xdc, 0xcc,
+       0x66, 0x7c, 0xc4, 0x8b, 0x78, 0x51, 0xd9, 0x81,
+       0xdf, 0xaa, 0xe2, 0x70, 0x2f, 0x02, 0x59, 0x10,
+       0x64, 0xb2, 0x93, 0x6d, 0x09, 0x23, 0xa9, 0x7d,
+       0x0a, 0xdb, 0x8a, 0x34, 0x53, 0xca, 0xe2, 0x6a,
+       0x6d, 0x39, 0xfb, 0x25, 0x5e, 0x38, 0x86, 0xeb,
+       0x4d, 0xa1, 0xc1, 0xea, 0xbd, 0xac, 0x1d, 0x14,
+       0x46, 0xac, 0x58, 0x86, 0x55, 0xec, 0x40, 0x9f,
+       0xdc, 0x4f, 0x80, 0xf2, 0x68, 0x0c, 0x81, 0xa3,
+       0xdf, 0x01, 0xa0, 0x62, 0x44, 0x9e, 0x20, 0x42,
+       0x89, 0x88, 0x24, 0xb2, 0x6a, 0x40, 0x11, 0x4b,
+       0x96, 0x33, 0xba, 0x0d, 0xae, 0x49, 0x98, 0x4b,
+       0x24, 0x16, 0x5f, 0xff, 0x85, 0x86, 0x4a, 0x09,
+       0xcd, 0xce, 0x30, 0xf2, 0xfa, 0xff, 0x74, 0x28,
+       0x40, 0x97, 0xa5, 0x56, 0x29, 0x74, 0x53, 0xa2,
+       0x34, 0xe4, 0xee, 0xe0, 0x45, 0xb6, 0xd8, 0xa7,
+       0x9b, 0xa0, 0x1a, 0x00, 0x2d, 0xff, 0x8d, 0x2f,
+       0xed, 0x70, 0x15, 0xc5, 0xe0, 0x11, 0xbb, 0xc8,
+       0xef, 0x5b, 0x2c, 0xb3, 0x12, 0x0f, 0xbe, 0x88,
+       0x7c, 0x98, 0x44, 0x3c, 0x65, 0x45, 0xbc, 0x20,
+       0xac, 0x07, 0xe2, 0x4c, 0x74, 0x2a, 0xb4, 0xb1,
+       0x0e, 0x47, 0x2a, 0xd6, 0x20, 0x19, 0xce, 0x75,
+       0x18, 0x45, 0x28, 0x90, 0x4f, 0x84, 0x42, 0x81,
+       0x37, 0xed, 0x1d, 0x0b, 0x48, 0xf7, 0x53, 0xe3,
+       0x92, 0xf2, 0xeb, 0xdf, 0x7a, 0x91, 0xdf, 0xe8,
+       0xdb, 0xb1, 0xc4, 0xfd, 0xfd, 0xc1, 0xad, 0x4e,
+       0xcc, 0xbe, 0x11, 0xe2, 0x76, 0x9b, 0x78, 0x2b,
+       0xb8, 0xf4, 0x0e, 0x9d, 0x05, 0xd6, 0x08, 0xd0,
+       0x76, 0x2c, 0xe8, 0x4d, 0xee, 0x3d, 0x31, 0xda,
+       0xc4, 0xf7, 0x01, 0x12, 0x8f, 0x5d, 0x94, 0xe6,
+       0xcb, 0x15, 0xfe, 0x53, 0x42, 0xb2, 0x51, 0x8c,
+       0x5d, 0xc7, 0x64, 0xde, 0x14, 0x8f, 0xaf, 0xc1,
+       0xaf, 0x36};
+
+/* HDCP 2.2 R2 */
+unsigned char cert_v22[522] = {
+       0x8b, 0xa4, 0x47, 0x42, 0xfb, 0xe4, 0x68, 0x63,
+       0x8a, 0xda, 0x97, 0x2d, 0xde, 0x9a, 0x8d, 0x1c,
+       0xb1, 0x65, 0x4b, 0x85, 0x8d, 0xe5, 0x46, 0xd6,
+       0xdb, 0x95, 0xa5, 0xf6, 0x66, 0x74, 0xea, 0x81,
+       0x0b, 0x9a, 0x58, 0x58, 0x66, 0x26, 0x86, 0xa6,
+       0xb4, 0x56, 0x2b, 0x29, 0x43, 0xe5, 0xbb, 0x81,
+       0x74, 0x86, 0xa7, 0xb7, 0x16, 0x2f, 0x07, 0xec,
+       0xd1, 0xb5, 0xf9, 0xae, 0x4f, 0x98, 0x89, 0xa9,
+       0x91, 0x7d, 0x58, 0x5b, 0x8d, 0x20, 0xd5, 0xc5,
+       0x08, 0x40, 0x3b, 0x86, 0xaf, 0xf4, 0xd6, 0xb9,
+       0x20, 0x95, 0xe8, 0x90, 0x3b, 0x8f, 0x9f, 0x36,
+       0x5b, 0x46, 0xb6, 0xd4, 0x1e, 0xf5, 0x05, 0x88,
+       0x80, 0x14, 0xe7, 0x2c, 0x77, 0x5d, 0x6e, 0x54,
+       0xe9, 0x65, 0x81, 0x5a, 0x68, 0x92, 0xa5, 0xd6,
+       0x40, 0x78, 0x11, 0x97, 0x65, 0xd7, 0x64, 0x36,
+       0x5e, 0x8d, 0x2a, 0x87, 0xa8, 0xeb, 0x7d, 0x06,
+       0x2c, 0x10, 0xf8, 0x0a, 0x7d, 0x01, 0x00, 0x01,
+       0x10, 0x00, 0x06, 0x40, 0x99, 0x8f, 0x5a, 0x54,
+       0x71, 0x23, 0xa7, 0x6a, 0x64, 0x3f, 0xbd, 0xdd,
+       0x52, 0xb2, 0x79, 0x6f, 0x88, 0x26, 0x94, 0x9e,
+       0xaf, 0xa4, 0xde, 0x7d, 0x8d, 0x88, 0x10, 0xc8,
+       0xf6, 0x56, 0xf0, 0x8f, 0x46, 0x28, 0x48, 0x55,
+       0x51, 0xc5, 0xaf, 0xa1, 0xa9, 0x9d, 0xac, 0x9f,
+       0xb1, 0x26, 0x4b, 0xeb, 0x39, 0xad, 0x88, 0x46,
+       0xaf, 0xbc, 0x61, 0xa8, 0x7b, 0xf9, 0x7b, 0x3e,
+       0xe4, 0x95, 0xd9, 0xa8, 0x79, 0x48, 0x51, 0x00,
+       0xbe, 0xa4, 0xb6, 0x96, 0x7f, 0x3d, 0xfd, 0x76,
+       0xa6, 0xb7, 0xbb, 0xb9, 0x77, 0xdc, 0x54, 0xfb,
+       0x52, 0x9c, 0x79, 0x8f, 0xed, 0xd4, 0xb1, 0xbc,
+       0x0f, 0x7e, 0xb1, 0x7e, 0x70, 0x6d, 0xfc, 0xb9,
+       0x7e, 0x66, 0x9a, 0x86, 0x23, 0x3a, 0x98, 0x5e,
+       0x32, 0x8d, 0x75, 0x18, 0x54, 0x64, 0x36, 0xdd,
+       0x92, 0x01, 0x39, 0x90, 0xb9, 0xe3, 0xaf, 0x6f,
+       0x98, 0xa5, 0xc0, 0x80, 0xc6, 0x2f, 0xa1, 0x02,
+       0xad, 0x8d, 0xf4, 0xd6, 0x66, 0x7b, 0x45, 0xe5,
+       0x74, 0x18, 0xb1, 0x27, 0x24, 0x01, 0x1e, 0xea,
+       0xd8, 0xf3, 0x79, 0x92, 0xe9, 0x03, 0xf5, 0x57,
+       0x8d, 0x65, 0x2a, 0x8d, 0x1b, 0xf0, 0xda, 0x58,
+       0x3f, 0x58, 0xa0, 0xf4, 0xb4, 0xbe, 0xcb, 0x21,
+       0x66, 0xe9, 0x21, 0x7c, 0x76, 0xf3, 0xc1, 0x7e,
+       0x2e, 0x7c, 0x3d, 0x61, 0x20, 0x1d, 0xc5, 0xc0,
+       0x71, 0x28, 0x2e, 0xb7, 0x0f, 0x1f, 0x7a, 0xc1,
+       0xd3, 0x6a, 0x1e, 0xa3, 0x54, 0x34, 0x8e, 0x0d,
+       0xd7, 0x96, 0x93, 0x78, 0x50, 0xc1, 0xee, 0x27,
+       0x72, 0x3a, 0xbd, 0x57, 0x22, 0xf0, 0xd7, 0x6d,
+       0x9d, 0x65, 0xc4, 0x07, 0x9c, 0x82, 0xa6, 0xd4,
+       0xf7, 0x6b, 0x9a, 0xe9, 0xc0, 0x6c, 0x4a, 0x4f,
+       0x6f, 0xbe, 0x8e, 0x01, 0x37, 0x50, 0x3a, 0x66,
+       0xd9, 0xe9, 0xd9, 0xf9, 0x06, 0x9e, 0x00, 0xa9,
+       0x84, 0xa0, 0x18, 0xb3, 0x44, 0x21, 0x24, 0xa3,
+       0x6c, 0xcd, 0xb7, 0x0f, 0x31, 0x2a, 0xe8, 0x15,
+       0xb6, 0x93, 0x6f, 0xb9, 0x86, 0xe5, 0x28, 0x01,
+       0x1a, 0x5e, 0x10, 0x3f, 0x1f, 0x4d, 0x35, 0xa2,
+       0x8d, 0xb8, 0x54, 0x26, 0x68, 0x3a, 0xcd, 0xcb,
+       0x5f, 0xfa, 0x37, 0x4a, 0x60, 0x10, 0xb1, 0x0a,
+       0xfe, 0xba, 0x9b, 0x96, 0x5d, 0x7e, 0x99, 0xcf,
+       0x01, 0x98, 0x65, 0x87, 0xad, 0x40, 0xd5, 0x82,
+       0x1d, 0x61, 0x54, 0xa2, 0xd3, 0x16, 0x3e, 0xf7,
+       0xe3, 0x05, 0x89, 0x8d, 0x8a, 0x50, 0x87, 0x47,
+       0xbe, 0x29, 0x18, 0x01, 0xb7, 0xc3, 0xdd, 0x43,
+       0x23, 0x7a, 0xcd, 0x85, 0x1d, 0x4e, 0xa9, 0xc0,
+       0x1a, 0xa4, 0x77, 0xab, 0xe7, 0x31, 0x9a, 0x33,
+       0x1b, 0x7a, 0x86, 0xe1, 0xe5, 0xca, 0x0c, 0x43,
+       0x1a, 0xfa, 0xec, 0x4c, 0x05, 0xc6, 0xd1, 0x43,
+       0x12, 0xf9, 0x4d, 0x3e, 0xf7, 0xd6, 0x05, 0x9c,
+       0x1c, 0xdd, /* end of cert_rx */
+       };
+#endif
+
+#define REPEATER       1
+#define NO_REPEATER    0
+
+/* Message Vector */
+/* Transmitter */
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_send_cert[524] = {
+       AKE_SEND_CERT,
+       REPEATER,
+       0x74, 0x5b, 0xb8, 0xbd, 0x04, 0xbc, 0x83, 0xc7,
+       0x95, 0x78, 0xf9, 0x0c, 0x91, 0x4b, 0x89, 0x38,
+       0x05, 0x5a, 0xa4, 0xac, 0x1f, 0xa8, 0x03, 0x93,
+       0x82, 0x79, 0x75, 0xaf, 0x66, 0x22, 0xde, 0x43,
+       0x80, 0x8d, 0xcd, 0x5d, 0x90, 0xb8, 0x3c, 0xb3,
+       0xd8, 0x9e, 0xb0, 0x0d, 0x09, 0x44, 0xf4, 0x3f,
+       0x5f, 0xab, 0xb9, 0xc4, 0xc9, 0x96, 0xef, 0x78,
+       0xb5, 0x8f, 0x69, 0x77, 0xb4, 0x7d, 0x08, 0x14,
+       0x9c, 0x81, 0xa0, 0x8f, 0x04, 0x1f, 0xa0, 0x88,
+       0xe1, 0x20, 0xc7, 0x34, 0x4a, 0x49, 0x35, 0x65,
+       0x99, 0xcf, 0x53, 0x19, 0xf0, 0xc6, 0x81, 0x76,
+       0x05, 0x5c, 0xb9, 0xde, 0xdd, 0xab, 0x3d, 0xb0,
+       0x92, 0xa1, 0x23, 0x4f, 0x0c, 0x71, 0x30, 0x42,
+       0x78, 0xf6, 0x55, 0xae, 0xbd, 0x36, 0x25, 0x8e,
+       0x25, 0x0d, 0x4e, 0x5e, 0x8e, 0x77, 0x6a, 0x60,
+       0xe3, 0xc1, 0xe9, 0xee, 0xcd, 0x2b, 0x9e, 0x18,
+       0x63, 0x97, 0xd4, 0xe6, 0x75, 0x01, 0x00, 0x01,
+       0x00, 0x00, 0x1d, 0x0a, 0x61, 0xea, 0xab, 0xf8,
+       0xa8, 0x2b, 0x02, 0x69, 0xa1, 0x34, 0xfd, 0x91,
+       0xac, 0x2b, 0xf2, 0x8f, 0x34, 0x8b, 0xd4, 0x84,
+       0xfa, 0x62, 0xbc, 0x01, 0x4a, 0x4a, 0xa2, 0xb2,
+       0x14, 0xbf, 0xb5, 0xf4, 0xdf, 0xac, 0x80, 0x93,
+       0x0d, 0x13, 0xec, 0x9c, 0xe5, 0xd8, 0x34, 0x70,
+       0x51, 0x9a, 0x66, 0x80, 0xeb, 0xbe, 0xcc, 0x7e,
+       0x45, 0xf0, 0xe6, 0x39, 0x63, 0x84, 0xc9, 0xb9,
+       0x8e, 0x8c, 0xaf, 0x9c, 0xa9, 0xd4, 0x0e, 0xeb,
+       0x9a, 0x57, 0x2a, 0x17, 0x41, 0xca, 0x97, 0xf3,
+       0x19, 0x96, 0xb5, 0x5d, 0x0f, 0x30, 0xa3, 0x84,
+       0xe5, 0x73, 0xa2, 0xed, 0x05, 0x69, 0x7a, 0x22,
+       0xce, 0x84, 0x1f, 0x3e, 0x39, 0x9e, 0x28, 0x76,
+       0xc9, 0xbc, 0x89, 0x5b, 0x70, 0xb1, 0x7b, 0xf4,
+       0xed, 0xb6, 0x74, 0x12, 0xab, 0x48, 0x29, 0x64,
+       0xce, 0x6c, 0x60, 0x04, 0xeb, 0xa9, 0x7a, 0xa2,
+       0x15, 0xa6, 0x58, 0x9a, 0xad, 0x32, 0xc7, 0x53,
+       0x39, 0xe5, 0xfe, 0xf0, 0x37, 0xa7, 0xa0, 0xc5,
+       0xff, 0xec, 0xd9, 0xb0, 0x05, 0xbb, 0x25, 0x13,
+       0xa0, 0xa4, 0xc7, 0x0b, 0x2a, 0x5d, 0xc6, 0x8f,
+       0x51, 0x11, 0xcb, 0x36, 0xed, 0x5c, 0x17, 0x7e,
+       0x22, 0x20, 0xc3, 0xeb, 0x40, 0x8c, 0x67, 0xbb,
+       0x1c, 0xd2, 0x47, 0xb0, 0xe0, 0xbd, 0xe7, 0x4c,
+       0xcd, 0x5d, 0xd5, 0x23, 0x12, 0xf8, 0x3b, 0x1d,
+       0x91, 0x3b, 0xf3, 0xc7, 0x60, 0xea, 0x90, 0x24,
+       0x48, 0xe5, 0x92, 0x21, 0x6c, 0xf6, 0xd9, 0x5e,
+       0x76, 0x8d, 0x2b, 0x86, 0xa6, 0x7c, 0x16, 0xae,
+       0xa8, 0x36, 0x08, 0xa0, 0x37, 0x14, 0x1a, 0xd7,
+       0x03, 0xe1, 0x40, 0x31, 0xca, 0x6c, 0x95, 0xe0,
+       0x10, 0xb0, 0x43, 0xcf, 0xb7, 0xe0, 0x30, 0x05,
+       0xb9, 0xac, 0xb7, 0x08, 0x68, 0xcd, 0x7e, 0x11,
+       0x47, 0x2a, 0x03, 0x3b, 0xeb, 0x74, 0xc8, 0x19,
+       0x62, 0x8b, 0x2f, 0x11, 0x91, 0xb6, 0x06, 0x4f,
+       0xe0, 0x2a, 0x44, 0x20, 0x43, 0x29, 0x13, 0x1f,
+       0xdd, 0xd0, 0x4a, 0x11, 0x6c, 0x0e, 0x83, 0xbf,
+       0x22, 0x62, 0x3b, 0xeb, 0xec, 0xd7, 0x76, 0x28,
+       0xba, 0x64, 0x88, 0x42, 0xc8, 0x73, 0xa7, 0x9e,
+       0x4a, 0x69, 0x3a, 0xb2, 0x0c, 0x4b, 0x3a, 0xd9,
+       0x50, 0xdb, 0x7c, 0x51, 0xee, 0x15, 0xe0, 0x6b,
+       0x2c, 0x63, 0xa6, 0x91, 0x57, 0xdd, 0xbf, 0x17,
+       0x47, 0x23, 0xad, 0x15, 0xcb, 0xb9, 0x91, 0x18,
+       0x0b, 0x51, 0x8f, 0xf9, 0x1c, 0x51, 0x67, 0xc1,
+       0x0b, 0x78, 0xf5, 0xd9, 0x55, 0xdc, 0x48, 0xe4,
+       0xc0, 0x83, 0xa5, 0xdf, 0x75, 0xe2, 0xdc, 0x88,
+       0xd2, 0xc6, 0xdd, 0xdf, 0x1f, 0x37, 0x90, 0x35,
+       0xf6, 0xfd, 0xda, 0xe0, 0x04, 0x32, 0x69, 0xc1,
+       0xaf, 0xd9, 0xf9, 0x11, 0xc5, 0xaa, 0x74, 0x58,
+       0x32, 0x1c, 0x71, 0xaa, 0xa7, 0x14, 0xfb, 0x23,
+       0x17, 0x22};
+
+uint8_t msg_rx_send_cert_v22[533] = {
+       0x74, 0x5b, 0xb8, 0xbd, 0x04, 0xaf, 0xb5, 0xc5,
+       0xc6, 0x7b, 0xc5, 0x3a, 0x34, 0x90, 0xa9, 0x54,
+       0xc0, 0x8f, 0xb7, 0xeb, 0xa1, 0x54, 0xd2, 0x4f,
+       0x22, 0xde, 0x83, 0xf5, 0x03, 0xa6, 0xc6, 0x68,
+       0x46, 0x9b, 0xc0, 0xb8, 0xc8, 0x6c, 0xdb, 0x26,
+       0xf9, 0x3c, 0x49, 0x2f, 0x02, 0xe1, 0x71, 0xdf,
+       0x4e, 0xf3, 0x0e, 0xc8, 0xbf, 0x22, 0x9d, 0x04,
+       0xcf, 0xbf, 0xa9, 0x0d, 0xff, 0x68, 0xab, 0x05,
+       0x6f, 0x1f, 0x12, 0x8a, 0x68, 0x62, 0xeb, 0xfe,
+       0xc9, 0xea, 0x9f, 0xa7, 0xfb, 0x8c, 0xba, 0xb1,
+       0xbd, 0x65, 0xac, 0x35, 0x9c, 0xa0, 0x33, 0xb1,
+       0xdd, 0xa6, 0x05, 0x36, 0xaf, 0x00, 0xa2, 0x7f,
+       0xbc, 0x07, 0xb2, 0xdd, 0xb5, 0xcc, 0x57, 0x5c,
+       0xdc, 0xc0, 0x95, 0x50, 0xe5, 0xff, 0x1f, 0x20,
+       0xdb, 0x59, 0x46, 0xfa, 0x47, 0xc4, 0xed, 0x12,
+       0x2e, 0x9e, 0x22, 0xbd, 0x95, 0xa9, 0x85, 0x59,
+       0xa1, 0x59, 0x3c, 0xc7, 0x83, 0x01, 0x00, 0x01,
+       0x10, 0x00, 0x0b, 0xa3, 0x73, 0x77, 0xdd, 0x03,
+       0x18, 0x03, 0x8a, 0x91, 0x63, 0x29, 0x1e, 0xa2,
+       0x95, 0x74, 0x42, 0x90, 0x78, 0xd0, 0x67, 0x25,
+       0xb6, 0x32, 0x2f, 0xcc, 0x23, 0x2b, 0xad, 0x21,
+       0x39, 0x3d, 0x14, 0xba, 0x37, 0xa3, 0x65, 0x14,
+       0x6b, 0x9c, 0xcf, 0x61, 0x20, 0x44, 0xa1, 0x07,
+       0xbb, 0xcf, 0xc3, 0x4e, 0x95, 0x5b, 0x10, 0xcf,
+       0xc7, 0x6f, 0xf1, 0xc3, 0x53, 0x7c, 0x63, 0xa1,
+       0x8c, 0xb2, 0xe8, 0xab, 0x2e, 0x96, 0x97, 0xc3,
+       0x83, 0x99, 0x70, 0xd3, 0xdc, 0x21, 0x41, 0xf6,
+       0x0a, 0xd1, 0x1a, 0xee, 0xf4, 0xcc, 0xeb, 0xfb,
+       0xa6, 0xaa, 0xb6, 0x9a, 0xaf, 0x1d, 0x16, 0x5e,
+       0xe2, 0x83, 0xa0, 0x4a, 0x41, 0xf6, 0x7b, 0x07,
+       0xbf, 0x47, 0x85, 0x28, 0x6c, 0xa0, 0x77, 0xa6,
+       0xa3, 0xd7, 0x85, 0xa5, 0xc4, 0xa7, 0xe7, 0x6e,
+       0xb5, 0x1f, 0x40, 0x72, 0x97, 0xfe, 0xc4, 0x81,
+       0x23, 0xa0, 0xc2, 0x90, 0xb3, 0x49, 0x24, 0xf5,
+       0xb7, 0x90, 0x2c, 0xbf, 0xfe, 0x04, 0x2e, 0x00,
+       0xa9, 0x5f, 0x86, 0x04, 0xca, 0xc5, 0x3a, 0xcc,
+       0x26, 0xd9, 0x39, 0x7e, 0xa9, 0x2d, 0x28, 0x6d,
+       0xc0, 0xcc, 0x6e, 0x81, 0x9f, 0xb9, 0xb7, 0x11,
+       0x33, 0x32, 0x23, 0x47, 0x98, 0x43, 0x0d, 0xa5,
+       0x1c, 0x59, 0xf3, 0xcd, 0xd2, 0x4a, 0xb7, 0x3e,
+       0x69, 0xd9, 0x21, 0x53, 0x9a, 0xf2, 0x6e, 0x77,
+       0x62, 0xae, 0x50, 0xda, 0x85, 0xc6, 0xaa, 0xc4,
+       0xb5, 0x1c, 0xcd, 0xa8, 0xa5, 0xdd, 0x6e, 0x62,
+       0x73, 0xff, 0x5f, 0x7b, 0xd7, 0x3c, 0x17, 0xba,
+       0x47, 0x0c, 0x89, 0x0e, 0x62, 0x79, 0x43, 0x94,
+       0xaa, 0xa8, 0x47, 0xf4, 0x4c, 0x38, 0x89, 0xa8,
+       0x81, 0xad, 0x23, 0x13, 0x27, 0x0c, 0x17, 0xcf,
+       0x3d, 0x83, 0x84, 0x57, 0x36, 0xe7, 0x22, 0x26,
+       0x2e, 0x76, 0xfd, 0x56, 0x80, 0x83, 0xf6, 0x70,
+       0xd4, 0x5c, 0x91, 0x48, 0x84, 0x7b, 0x18, 0xdb,
+       0x0e, 0x15, 0x3b, 0x49, 0x26, 0x23, 0xe6, 0xa3,
+       0xe2, 0xc6, 0x3a, 0x23, 0x57, 0x66, 0xb0, 0x72,
+       0xb8, 0x12, 0x17, 0x4f, 0x86, 0xfe, 0x48, 0x0d,
+       0x53, 0xea, 0xfe, 0x31, 0x48, 0x7d, 0x86, 0xde,
+       0xeb, 0x82, 0x86, 0x1e, 0x62, 0x03, 0x98, 0x59,
+       0x00, 0x37, 0xeb, 0x61, 0xe9, 0xf9, 0x7a, 0x40,
+       0x78, 0x1c, 0xba, 0xbc, 0x0b, 0x88, 0xfb, 0xfd,
+       0x9d, 0xd5, 0x01, 0x11, 0x94, 0xe0, 0x35, 0xbe,
+       0x33, 0xe8, 0xe5, 0x36, 0xfb, 0x9c, 0x45, 0xcb,
+       0x75, 0xaf, 0xd6, 0x35, 0xff, 0x78, 0x92, 0x7f,
+       0xa1, 0x7c, 0xa8, 0xfc, 0xb7, 0xf7, 0xa8, 0x52,
+       0xa9, 0xc6, 0x84, 0x72, 0x3d, 0x1c, 0xc9, 0xdf,
+       0x35, 0xc6, 0xe6, 0x00, 0xe1, 0x48, 0x72, 0xce,
+       0x83, 0x1b, 0xcc, 0xf8, 0x33, 0x2d, 0x4f, 0x98,
+       0x75, 0x00, 0x3c, 0x41, 0xdf, 0x7a, 0xed, 0x38,
+       0x53, 0xb1, /* end of Cert Rx */
+       0x3b, 0xa0, 0xbe, 0xde, 0x0c, 0x46, 0xa9, 0x91, /* rrx */
+       0x02, 0x00, 0x03 /* RxCaps */
+       };
+
+/* Receiver Publickey Certificate */
+#else
+uint8_t msg_rx_send_cert[524] = {
+       AKE_SEND_CERT,
+       NO_REPEATER,
+       /* Receiver Publickey Certificate */
+       0x8b, 0xa4, 0x47, 0x42, 0xfb, 0xc9, 0x1b, 0x82,
+       0xe2, 0x76, 0x7f, 0x90, 0x4f, 0xe9, 0x12, 0x33,
+       0x7c, 0x21, 0x1f, 0x7b, 0x25, 0xda, 0x76, 0xde,
+       0xae, 0x59, 0x70, 0xf7, 0xc2, 0xe7, 0xe0, 0x4a,
+       0xcf, 0xbd, 0x5b, 0xba, 0x1c, 0x36, 0x4e, 0xe3,
+       0x78, 0x4c, 0x92, 0x6a, 0x3c, 0xd8, 0xc1, 0xe9,
+       0x51, 0xa9, 0x35, 0xeb, 0xd8, 0xe8, 0xd5, 0x3e,
+       0x3b, 0x1d, 0x00, 0xc1, 0x16, 0x16, 0xd0, 0x58,
+       0xeb, 0x2a, 0x4b, 0xa0, 0x76, 0x9c, 0xd0, 0xe4,
+       0xb2, 0x23, 0xdc, 0xaa, 0x37, 0x07, 0xe5, 0x85,
+       0x1a, 0xaa, 0x13, 0x55, 0x01, 0x4e, 0xed, 0x88,
+       0xca, 0x3f, 0xfb, 0xc5, 0x58, 0x46, 0x91, 0xec,
+       0x35, 0x99, 0x08, 0x1c, 0xa1, 0x22, 0x64, 0xe8,
+       0x3c, 0x2e, 0x70, 0xdf, 0xa9, 0x10, 0x14, 0x81,
+       0x46, 0xa2, 0x38, 0x08, 0xef, 0x1b, 0xd2, 0x46,
+       0xee, 0x38, 0x0d, 0x6d, 0x92, 0xd3, 0xf2, 0x02,
+       0xe7, 0xe4, 0x29, 0xad, 0x0d, 0x01, 0x00, 0x01,
+       0x00, 0x00, 0x91, 0x18, 0x81, 0xa5, 0xcd, 0xab,
+       0x78, 0x50, 0xad, 0x1d, 0x3b, 0x77, 0xbe, 0x51,
+       0x32, 0x9f, 0x04, 0xe6, 0x3e, 0xf7, 0x01, 0x39,
+       0xf2, 0x59, 0x98, 0x75, 0x9d, 0x29, 0x12, 0x33,
+       0x39, 0xb4, 0x80, 0x91, 0x9d, 0x6a, 0xff, 0x0d,
+       0x5c, 0x59, 0x22, 0x43, 0x77, 0xfc, 0xed, 0xc2,
+       0x40, 0x9d, 0xe2, 0xd1, 0x4b, 0xff, 0x02, 0x78,
+       0x36, 0xd3, 0xad, 0xcb, 0xa6, 0xd3, 0xd3, 0x9d,
+       0xcc, 0xff, 0xcb, 0x3c, 0xa3, 0xcb, 0xfd, 0xdf,
+       0xcf, 0xe2, 0x85, 0xa8, 0xbd, 0xa2, 0xf6, 0x60,
+       0x06, 0xb2, 0x9b, 0x53, 0xc4, 0xd6, 0x22, 0xbd,
+       0x65, 0x3c, 0x6f, 0x40, 0x01, 0x7c, 0x2c, 0x78,
+       0x89, 0x31, 0x70, 0x47, 0x56, 0x88, 0xf5, 0x56,
+       0x33, 0xf2, 0x0a, 0x91, 0x27, 0xb1, 0x68, 0x5f,
+       0x84, 0x98, 0x1d, 0x37, 0xbd, 0x69, 0x11, 0x6d,
+       0x60, 0xca, 0x01, 0x44, 0xbe, 0xfa, 0x92, 0x1f,
+       0xec, 0x15, 0xbe, 0x37, 0x68, 0xd1, 0xdc, 0xcc,
+       0x66, 0x7c, 0xc4, 0x8b, 0x78, 0x51, 0xd9, 0x81,
+       0xdf, 0xaa, 0xe2, 0x70, 0x2f, 0x02, 0x59, 0x10,
+       0x64, 0xb2, 0x93, 0x6d, 0x09, 0x23, 0xa9, 0x7d,
+       0x0a, 0xdb, 0x8a, 0x34, 0x53, 0xca, 0xe2, 0x6a,
+       0x6d, 0x39, 0xfb, 0x25, 0x5e, 0x38, 0x86, 0xeb,
+       0x4d, 0xa1, 0xc1, 0xea, 0xbd, 0xac, 0x1d, 0x14,
+       0x46, 0xac, 0x58, 0x86, 0x55, 0xec, 0x40, 0x9f,
+       0xdc, 0x4f, 0x80, 0xf2, 0x68, 0x0c, 0x81, 0xa3,
+       0xdf, 0x01, 0xa0, 0x62, 0x44, 0x9e, 0x20, 0x42,
+       0x89, 0x88, 0x24, 0xb2, 0x6a, 0x40, 0x11, 0x4b,
+       0x96, 0x33, 0xba, 0x0d, 0xae, 0x49, 0x98, 0x4b,
+       0x24, 0x16, 0x5f, 0xff, 0x85, 0x86, 0x4a, 0x09,
+       0xcd, 0xce, 0x30, 0xf2, 0xfa, 0xff, 0x74, 0x28,
+       0x40, 0x97, 0xa5, 0x56, 0x29, 0x74, 0x53, 0xa2,
+       0x34, 0xe4, 0xee, 0xe0, 0x45, 0xb6, 0xd8, 0xa7,
+       0x9b, 0xa0, 0x1a, 0x00, 0x2d, 0xff, 0x8d, 0x2f,
+       0xed, 0x70, 0x15, 0xc5, 0xe0, 0x11, 0xbb, 0xc8,
+       0xef, 0x5b, 0x2c, 0xb3, 0x12, 0x0f, 0xbe, 0x88,
+       0x7c, 0x98, 0x44, 0x3c, 0x65, 0x45, 0xbc, 0x20,
+       0xac, 0x07, 0xe2, 0x4c, 0x74, 0x2a, 0xb4, 0xb1,
+       0x0e, 0x47, 0x2a, 0xd6, 0x20, 0x19, 0xce, 0x75,
+       0x18, 0x45, 0x28, 0x90, 0x4f, 0x84, 0x42, 0x81,
+       0x37, 0xed, 0x1d, 0x0b, 0x48, 0xf7, 0x53, 0xe3,
+       0x92, 0xf2, 0xeb, 0xdf, 0x7a, 0x91, 0xdf, 0xe8,
+       0xdb, 0xb1, 0xc4, 0xfd, 0xfd, 0xc1, 0xad, 0x4e,
+       0xcc, 0xbe, 0x11, 0xe2, 0x76, 0x9b, 0x78, 0x2b,
+       0xb8, 0xf4, 0x0e, 0x9d, 0x05, 0xd6, 0x08, 0xd0,
+       0x76, 0x2c, 0xe8, 0x4d, 0xee, 0x3d, 0x31, 0xda,
+       0xc4, 0xf7, 0x01, 0x12, 0x8f, 0x5d, 0x94, 0xe6,
+       0xcb, 0x15, 0xfe, 0x53, 0x42, 0xb2, 0x51, 0x8c,
+       0x5d, 0xc7, 0x64, 0xde, 0x14, 0x8f, 0xaf, 0xc1,
+       0xaf, 0x36};
+
+uint8_t msg_rx_send_cert_v22[533] = {
+       0x8b, 0xa4, 0x47, 0x42, 0xfb, 0xe4, 0x68, 0x63,
+       0x8a, 0xda, 0x97, 0x2d, 0xde, 0x9a, 0x8d, 0x1c,
+       0xb1, 0x65, 0x4b, 0x85, 0x8d, 0xe5, 0x46, 0xd6,
+       0xdb, 0x95, 0xa5, 0xf6, 0x66, 0x74, 0xea, 0x81,
+       0x0b, 0x9a, 0x58, 0x58, 0x66, 0x26, 0x86, 0xa6,
+       0xb4, 0x56, 0x2b, 0x29, 0x43, 0xe5, 0xbb, 0x81,
+       0x74, 0x86, 0xa7, 0xb7, 0x16, 0x2f, 0x07, 0xec,
+       0xd1, 0xb5, 0xf9, 0xae, 0x4f, 0x98, 0x89, 0xa9,
+       0x91, 0x7d, 0x58, 0x5b, 0x8d, 0x20, 0xd5, 0xc5,
+       0x08, 0x40, 0x3b, 0x86, 0xaf, 0xf4, 0xd6, 0xb9,
+       0x20, 0x95, 0xe8, 0x90, 0x3b, 0x8f, 0x9f, 0x36,
+       0x5b, 0x46, 0xb6, 0xd4, 0x1e, 0xf5, 0x05, 0x88,
+       0x80, 0x14, 0xe7, 0x2c, 0x77, 0x5d, 0x6e, 0x54,
+       0xe9, 0x65, 0x81, 0x5a, 0x68, 0x92, 0xa5, 0xd6,
+       0x40, 0x78, 0x11, 0x97, 0x65, 0xd7, 0x64, 0x36,
+       0x5e, 0x8d, 0x2a, 0x87, 0xa8, 0xeb, 0x7d, 0x06,
+       0x2c, 0x10, 0xf8, 0x0a, 0x7d, 0x01, 0x00, 0x01,
+       0x10, 0x00, 0x06, 0x40, 0x99, 0x8f, 0x5a, 0x54,
+       0x71, 0x23, 0xa7, 0x6a, 0x64, 0x3f, 0xbd, 0xdd,
+       0x52, 0xb2, 0x79, 0x6f, 0x88, 0x26, 0x94, 0x9e,
+       0xaf, 0xa4, 0xde, 0x7d, 0x8d, 0x88, 0x10, 0xc8,
+       0xf6, 0x56, 0xf0, 0x8f, 0x46, 0x28, 0x48, 0x55,
+       0x51, 0xc5, 0xaf, 0xa1, 0xa9, 0x9d, 0xac, 0x9f,
+       0xb1, 0x26, 0x4b, 0xeb, 0x39, 0xad, 0x88, 0x46,
+       0xaf, 0xbc, 0x61, 0xa8, 0x7b, 0xf9, 0x7b, 0x3e,
+       0xe4, 0x95, 0xd9, 0xa8, 0x79, 0x48, 0x51, 0x00,
+       0xbe, 0xa4, 0xb6, 0x96, 0x7f, 0x3d, 0xfd, 0x76,
+       0xa6, 0xb7, 0xbb, 0xb9, 0x77, 0xdc, 0x54, 0xfb,
+       0x52, 0x9c, 0x79, 0x8f, 0xed, 0xd4, 0xb1, 0xbc,
+       0x0f, 0x7e, 0xb1, 0x7e, 0x70, 0x6d, 0xfc, 0xb9,
+       0x7e, 0x66, 0x9a, 0x86, 0x23, 0x3a, 0x98, 0x5e,
+       0x32, 0x8d, 0x75, 0x18, 0x54, 0x64, 0x36, 0xdd,
+       0x92, 0x01, 0x39, 0x90, 0xb9, 0xe3, 0xaf, 0x6f,
+       0x98, 0xa5, 0xc0, 0x80, 0xc6, 0x2f, 0xa1, 0x02,
+       0xad, 0x8d, 0xf4, 0xd6, 0x66, 0x7b, 0x45, 0xe5,
+       0x74, 0x18, 0xb1, 0x27, 0x24, 0x01, 0x1e, 0xea,
+       0xd8, 0xf3, 0x79, 0x92, 0xe9, 0x03, 0xf5, 0x57,
+       0x8d, 0x65, 0x2a, 0x8d, 0x1b, 0xf0, 0xda, 0x58,
+       0x3f, 0x58, 0xa0, 0xf4, 0xb4, 0xbe, 0xcb, 0x21,
+       0x66, 0xe9, 0x21, 0x7c, 0x76, 0xf3, 0xc1, 0x7e,
+       0x2e, 0x7c, 0x3d, 0x61, 0x20, 0x1d, 0xc5, 0xc0,
+       0x71, 0x28, 0x2e, 0xb7, 0x0f, 0x1f, 0x7a, 0xc1,
+       0xd3, 0x6a, 0x1e, 0xa3, 0x54, 0x34, 0x8e, 0x0d,
+       0xd7, 0x96, 0x93, 0x78, 0x50, 0xc1, 0xee, 0x27,
+       0x72, 0x3a, 0xbd, 0x57, 0x22, 0xf0, 0xd7, 0x6d,
+       0x9d, 0x65, 0xc4, 0x07, 0x9c, 0x82, 0xa6, 0xd4,
+       0xf7, 0x6b, 0x9a, 0xe9, 0xc0, 0x6c, 0x4a, 0x4f,
+       0x6f, 0xbe, 0x8e, 0x01, 0x37, 0x50, 0x3a, 0x66,
+       0xd9, 0xe9, 0xd9, 0xf9, 0x06, 0x9e, 0x00, 0xa9,
+       0x84, 0xa0, 0x18, 0xb3, 0x44, 0x21, 0x24, 0xa3,
+       0x6c, 0xcd, 0xb7, 0x0f, 0x31, 0x2a, 0xe8, 0x15,
+       0xb6, 0x93, 0x6f, 0xb9, 0x86, 0xe5, 0x28, 0x01,
+       0x1a, 0x5e, 0x10, 0x3f, 0x1f, 0x4d, 0x35, 0xa2,
+       0x8d, 0xb8, 0x54, 0x26, 0x68, 0x3a, 0xcd, 0xcb,
+       0x5f, 0xfa, 0x37, 0x4a, 0x60, 0x10, 0xb1, 0x0a,
+       0xfe, 0xba, 0x9b, 0x96, 0x5d, 0x7e, 0x99, 0xcf,
+       0x01, 0x98, 0x65, 0x87, 0xad, 0x40, 0xd5, 0x82,
+       0x1d, 0x61, 0x54, 0xa2, 0xd3, 0x16, 0x3e, 0xf7,
+       0xe3, 0x05, 0x89, 0x8d, 0x8a, 0x50, 0x87, 0x47,
+       0xbe, 0x29, 0x18, 0x01, 0xb7, 0xc3, 0xdd, 0x43,
+       0x23, 0x7a, 0xcd, 0x85, 0x1d, 0x4e, 0xa9, 0xc0,
+       0x1a, 0xa4, 0x77, 0xab, 0xe7, 0x31, 0x9a, 0x33,
+       0x1b, 0x7a, 0x86, 0xe1, 0xe5, 0xca, 0x0c, 0x43,
+       0x1a, 0xfa, 0xec, 0x4c, 0x05, 0xc6, 0xd1, 0x43,
+       0x12, 0xf9, 0x4d, 0x3e, 0xf7, 0xd6, 0x05, 0x9c,
+       0x1c, 0xdd, /* end of cert_rx */
+
+       0xe1, 0x7a, 0xb0, 0xfd, 0x0f, 0x54, 0x40, 0x52, /* rrx */
+       0x02, 0x00, 0x02 /* RxCaps */
+};
+#endif
+
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_receiver_info[6] = {
+       AKE_RECEIVER_INFO,
+       0x00, 0x06,
+       0x01,
+       0x00,
+       LC_PRECOMPUTE_SUPPORT
+};
+#else
+uint8_t msg_rx_receiver_info[6] = {
+       AKE_RECEIVER_INFO,
+       0x00, 0x06,
+       0x01,
+       0x00,
+       LC_PRECOMPUTE_SUPPORT
+               /* LC_PRECOMPUTE_NOT_SUPPORT */
+};
+#endif
+
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_send_rrx[9] = {
+       AKE_SEND_RRX,
+       0x3b, 0xa0, 0xbe, 0xde, 0x0c, 0x46, 0xa9, 0x91};
+#else
+uint8_t msg_rx_send_rrx[9] = {
+       AKE_SEND_RRX,
+       0xe1, 0x7a, 0xb0, 0xfd, 0x0f, 0x54, 0x40, 0x52};
+#endif
+
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_send_h_prime[33] = {
+       AKE_SEND_H_PRIME,
+       0xc3, 0xc0, 0x39, 0x2d, 0xc8, 0x66, 0xa2, 0xc3,
+       0x98, 0x07, 0xb4, 0xbc, 0x3c, 0xbb, 0x7a, 0xd0,
+       0xe6, 0x1f, 0x49, 0xd5, 0xe7, 0x04, 0x7e, 0x9a,
+       0xea, 0x8c, 0xac, 0xf9, 0xd7, 0xc8, 0xcd, 0x89};
+
+uint8_t msg_rx_send_h_prime_v22[32] = {
+       0x2e, 0xf5, 0xed, 0xf8, 0x7f, 0xd8, 0xa3, 0xd0,
+       0xf4, 0xa9, 0xd8, 0xac, 0x3a, 0xd0, 0xb4, 0x56,
+       0x2e, 0x32, 0x19, 0x11, 0x41, 0x16, 0xf1, 0xef,
+       0x0f, 0x02, 0x3d, 0x3a, 0x78, 0xe2, 0x2a, 0xc6};
+#else
+uint8_t msg_rx_send_h_prime[33] = {
+       AKE_SEND_H_PRIME,
+       0xee, 0x6f, 0x40, 0x74, 0xeb, 0x1b, 0xd0, 0x7b,
+       0x35, 0x15, 0xb0, 0xf8, 0x28, 0x6a, 0xb5, 0x66,
+       0x96, 0xe9, 0x39, 0x2b, 0xd7, 0x62, 0xbe, 0xd4,
+       0x6a, 0x92, 0xd8, 0xd0, 0xa4, 0x18, 0x4d, 0x42};
+
+uint8_t msg_rx_send_h_prime_v22[32] = {
+       0x82, 0xb8, 0x1a, 0xca, 0xed, 0xfc, 0x87, 0x72,
+       0x7d, 0x17, 0x23, 0x53, 0xcb, 0x81, 0x83, 0xbf,
+       0xdb, 0xba, 0xfb, 0x90, 0xb2, 0x4e, 0x96, 0xfe,
+       0xba, 0x6d, 0xad, 0x67, 0xaa, 0x2b, 0x2a, 0x56};
+#endif
+
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_send_pairing_info[17] = {
+       AKE_SEND_PAIRING_INFO,
+       0xce, 0x82, 0xf8, 0x36, 0x56, 0xcb, 0xbb, 0xaf,
+       0x40, 0xba, 0x83, 0x17, 0x8c, 0xd3, 0xa4, 0xa6};
+
+uint8_t msg_rx_send_pairing_info_v22[16] = {
+       0xb8, 0x9f, 0xf9, 0x72, 0x6a, 0x6f, 0x2c, 0x1e,
+       0x29, 0xb6, 0x44, 0x8d, 0xdc, 0xa3, 0x10, 0xbd};
+#else
+uint8_t msg_rx_send_pairing_info[17] = {
+       AKE_SEND_PAIRING_INFO,
+       0x2e, 0xec, 0xe5, 0x58, 0xe7, 0x8f, 0x1a, 0x96,
+       0xd3, 0xbd, 0x40, 0x14, 0xa6, 0x7e, 0x29, 0x3a};
+
+uint8_t msg_rx_send_pairing_info_v22[16] = {
+       0xe6, 0x57, 0x8e, 0xbc, 0xc7, 0x68, 0x44, 0x87,
+       0x88, 0x8a, 0x9b, 0xd7, 0xd6, 0xae, 0x38, 0xbe};
+#endif
+
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_lc_send_l_prime[33] = {
+       LC_SEND_L_PRIME,
+       0x1b, 0x57, 0x26, 0xde, 0xb5, 0x65, 0x1c, 0x77,
+       0x31, 0x70, 0xd0, 0x09, 0x4a, 0x14, 0xeb, 0x0d,
+       0x1f, 0x1c, 0xab, 0xa9, 0xfe, 0x84, 0xfd, 0xd4,
+       0xeb, 0x17, 0x52, 0xe3, 0xe8, 0xd0, 0xd6, 0x36};
+
+uint8_t msg_rx_lc_send_l_prime_v22[32] = {
+       0xbc, 0x20, 0x92, 0x33, 0x54, 0x91, 0xc1, 0x9e,
+       0xa4, 0xde, 0x8b, 0x30, 0x49, 0xc2, 0x06, 0x6a,
+       0xd8, 0x11, 0xa2, 0x2a, 0xb1, 0x46, 0xdf, 0x74,
+       0x58, 0x47, 0x05, 0xa8, 0xb7, 0x67, 0xfb, 0xdd};
+#else
+uint8_t msg_rx_lc_send_l_prime[33] = {
+       LC_SEND_L_PRIME,
+       0x19, 0x16, 0xb1, 0x59, 0x73, 0xbe, 0xe3, 0x67,
+       0xf0, 0x56, 0x50, 0x51, 0x44, 0x0f, 0x53, 0xa2,
+       0xdf, 0x53, 0x8d, 0xce, 0xe2, 0x58, 0x1f, 0x65,
+       0xf3, 0xbf, 0x03, 0x0e, 0x68, 0x14, 0xe0, 0xe4};
+
+uint8_t msg_rx_lc_send_l_prime_v22[32] = {
+       0xf2, 0x0f, 0x13, 0x6e, 0x85, 0x53, 0xc1, 0x0c,
+       0xd3, 0xdd, 0xb2, 0xf9, 0x6d, 0x33, 0x31, 0xf9,
+       0xcb, 0x6e, 0x97, 0x8c, 0xcd, 0x5e, 0xda, 0x13,
+       0xdd, 0xea, 0x41, 0x44, 0x10, 0x9b, 0x51, 0xb0};
+#endif
+
+#ifdef TEST_VECTOR_1
+uint8_t msg_rx_rtt_ready[1] = {RTT_READY};
+#else
+uint8_t msg_rx_rtt_ready[1] = {RTT_READY};
+#endif
+
+#ifdef TEST_VECTOR_1
+/* should enter the test vector of master key in trustlet */
+uint8_t tv_emkey[128] = {
+       0x75, 0xd1, 0x07, 0xf6, 0x9b, 0x7f, 0x1e, 0xbd,
+       0x84, 0x2a, 0x33, 0xe2, 0x89, 0x03, 0x75, 0xb1,
+       0x27, 0x3c, 0xc2, 0xf1, 0xda, 0xf8, 0x76, 0x94,
+       0x9b, 0xe0, 0x5e, 0x35, 0xa4, 0xb3, 0x32, 0xa0,
+       0xb5, 0xea, 0x75, 0xef, 0x6f, 0x3a, 0xd8, 0x31,
+       0x3a, 0xdd, 0xc5, 0x9b, 0xde, 0xa3, 0xbb, 0x00,
+       0x39, 0x29, 0x48, 0x93, 0x76, 0x2a, 0xe9, 0xe1,
+       0xb5, 0x17, 0x0c, 0x64, 0x6b, 0xd4, 0x12, 0x8f,
+       0x9b, 0xe1, 0x07, 0xb5, 0xa7, 0x9e, 0xd6, 0xfd,
+       0x6e, 0x28, 0xc8, 0x5b, 0xd0, 0xc4, 0x01, 0xe7,
+       0x69, 0xf4, 0x70, 0xab, 0xb8, 0x91, 0x2a, 0x31,
+       0x4b, 0x86, 0xfd, 0x96, 0x80, 0x20, 0x19, 0x7b,
+       0x73, 0xad, 0x7e, 0x78, 0xae, 0xe1, 0x71, 0x07,
+       0x07, 0x50, 0x2d, 0x3f, 0x43, 0xff, 0xd4, 0x50,
+       0x4e, 0xd7, 0x70, 0xfe, 0xcf, 0xf2, 0x25, 0x82,
+       0xba, 0x43, 0x9e, 0x61, 0xd6, 0xf5, 0x02, 0x84};
+
+uint8_t tv_rrx[8] = {
+       0x3b, 0xa0, 0xbe, 0xde, 0x0c, 0x46, 0xa9, 0x91};
+
+uint8_t tv_hmac_rrx[32] = {
+       0xc3, 0xc0, 0x39, 0x2d, 0xc8, 0x66, 0xa2, 0xc3,
+       0x98, 0x07, 0xb4, 0xbc, 0x3c, 0xbb, 0x7a, 0xd0,
+       0xe6, 0x1f, 0x49, 0xd5, 0xe7, 0x04, 0x7e, 0x9a,
+       0xea, 0x8c, 0xac, 0xf9, 0xd7, 0xc8, 0xcd, 0x89};
+
+uint8_t tv_pairing_ekh[16] = {
+       0xce, 0x82, 0xf8, 0x36, 0x56, 0xcb, 0xbb, 0xaf,
+       0x40, 0xba, 0x83, 0x17, 0x8c, 0xd3, 0xa4, 0xa6};
+
+/* todo: need to check */
+uint8_t tv_pairing_m[16] = {
+       0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49,
+       0x3b, 0xa0, 0xbe, 0xde, 0x0c, 0x46, 0xa9, 0x91};
+
+uint8_t tv_lc_hmac[32] = {
+       0x1b, 0x57, 0x26, 0xde, 0xb5, 0x65, 0x1c, 0x77,
+       0x31, 0x70, 0xd0, 0x09, 0x4a, 0x14, 0xeb, 0x0d,
+       0x1f, 0x1c, 0xab, 0xa9, 0xfe, 0x84, 0xfd, 0xd4,
+       0xeb, 0x17, 0x52, 0xe3, 0xe8, 0xd0, 0xd6, 0x36};
+
+uint8_t tv_lc_lsb16_hmac[32] = {
+       0x1f, 0x1c, 0xab, 0xa9, 0xfe, 0x84, 0xfd, 0xd4,
+       0xeb, 0x17, 0x52, 0xe3, 0xe8, 0xd0, 0xd6, 0x36};
+
+uint8_t tv_eskey[16] = {
+       0xcb, 0x92, 0xf6, 0x2a, 0x0a, 0x24, 0x29, 0x4f,
+       0x7e, 0x5f, 0x6a, 0x69, 0xc7, 0xa2, 0xeb, 0x05};
+
+uint8_t tv_plain[32] = {
+       0x00, 0x00, 0x01, 0x00, 0x01, 0x1b, 0x3c, 0x5b,
+       0xb8, 0x00, 0x00, 0x00, 0x01, 0xb5, 0x85, 0x44,
+       0x3b, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb2,
+       0x44, 0x54, 0x47, 0x31, 0x41, 0xfe, 0x00, 0x00};
+
+uint8_t tv_cipher[32] = {
+       0x21, 0x8b, 0x92, 0xa3, 0x28, 0xc9, 0x84, 0x36,
+       0x17, 0x46, 0x4e, 0xd8, 0x68, 0x7a, 0x67, 0xb0,
+       0xeb, 0x66, 0x72, 0x30, 0x24, 0x5b, 0x70, 0x2a,
+       0x25, 0x86, 0xab, 0x4a, 0x7a, 0x9a, 0x3a, 0x4f};
+
+uint8_t tv_emkey_v22[128] = {
+       0x9b, 0x9f, 0x80, 0x19, 0xad, 0x0e, 0xa2, 0xf0,
+       0xdd, 0xa0, 0x29, 0x33, 0xd9, 0x6d, 0x1c, 0x77,
+       0x31, 0x37, 0x57, 0xe0, 0xe5, 0xb2, 0xbd, 0xdd,
+       0x36, 0x3e, 0x38, 0x4e, 0x7d, 0x40, 0x78, 0x66,
+       0x97, 0x7a, 0x4c, 0xce, 0xc5, 0xc7, 0x5d, 0x01,
+       0x57, 0x26, 0xcc, 0xa2, 0xf6, 0xde, 0x34, 0xdd,
+       0x29, 0xbe, 0x5e, 0x31, 0xe8, 0xf1, 0x34, 0xe8,
+       0x1a, 0x63, 0xa3, 0x6d, 0x46, 0xdc, 0x0a, 0x06,
+       0x08, 0x99, 0x9d, 0xdb, 0x3c, 0xa2, 0x9c, 0x04,
+       0xdd, 0x4e, 0xd9, 0x02, 0x7d, 0x20, 0x54, 0xec,
+       0xca, 0x86, 0x42, 0x1b, 0x18, 0xda, 0x30, 0x9c,
+       0xc4, 0xcb, 0xac, 0xb4, 0x54, 0xde, 0x84, 0x68,
+       0x71, 0x53, 0x6d, 0x92, 0x17, 0xca, 0x08, 0x8a,
+       0x7a, 0xf9, 0x98, 0x9a, 0xb6, 0x7b, 0x22, 0x92,
+       0xac, 0x7d, 0x0d, 0x6b, 0xd6, 0x7f, 0x31, 0xab,
+       0xf0, 0x10, 0xc5, 0x2a, 0x0f, 0x6d, 0x27, 0xa0};
+
+uint8_t tv_pairing_ekh_v22[16] = {
+       0xb8, 0x9f, 0xf9, 0x72, 0x6a, 0x6f, 0x2c, 0x1e,
+       0x29, 0xb6, 0x44, 0x8d, 0xdc, 0xa3, 0x10, 0xbd};
+
+uint8_t tv_pairing_m_v22[16] = {
+       0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49,
+       0x3b, 0xa0, 0xbe, 0xde, 0x0c, 0x46, 0xa9, 0x91};
+
+uint8_t tv_ske_eskey_v22[16] = {
+       0x4c, 0x32, 0x47, 0x12, 0xc4, 0xbe, 0xc6, 0x69,
+       0x0a, 0xc2, 0x19, 0x64, 0xde, 0x91, 0xf1, 0x83};
+
+uint8_t tv_rcvid_list_v22[36] = {
+       0x02, 0x31, /* RxInfo */
+       0x00, 0x00, 0x00, /* seq_num_V */
+       0xbc, 0xcc, 0x7d, 0x16, 0xe6, 0xbc, 0xb9, 0x02, /* V' */
+       0x60, 0x08, 0x1d, 0xf7, 0x4a, 0xb4, 0x5c, 0x8a,
+       0x47, 0x8e, 0x71, 0xe2, 0x0f, 0x35, 0x79, 0x6a, /* Receiver id */
+       0x17, 0x0e, 0x74, 0xe8, 0x53, 0x97, 0xa2};
+
+uint8_t tv_rpauth_v_v22[16] = {
+       0x63, 0x6d, 0xc5, 0x08, 0x4d, 0x6c, 0xb1, 0x0e,
+       0x93, 0xa5, 0x28, 0x67, 0x0f, 0x34, 0x1f, 0x88};
+
+uint8_t tv_rpauth_stream_manage_v22[7] = {
+       0x00, 0x00, 0x00, /* seq_num_M */
+       0x00, 0x01, /* k */
+       0x00, 0x01};
+
+uint8_t tv_rpauth_stream_ready_v22[32] = {
+       0xdd, 0x26, 0xe9, 0x52, 0x6e, 0x0e, 0x1d, 0x69,
+       0xc8, 0x84, 0xe4, 0xcc, 0xc8, 0x09, 0xaa, 0xc7,
+       0x71, 0xe9, 0x97, 0xb5, 0x61, 0x89, 0x09, 0x6e,
+       0x4d, 0x94, 0x24, 0xc2, 0x1b, 0x64, 0x58, 0xc6};
+#else
+uint8_t tv_emkey[128] = {
+       0x78, 0x73, 0x6b, 0x24, 0xd6, 0x26, 0xfd, 0x11,
+       0x36, 0xb5, 0x55, 0x5a, 0xa8, 0xbe, 0x46, 0x9e,
+       0x69, 0xa1, 0xef, 0x19, 0xde, 0xd2, 0x43, 0x33,
+       0x7b, 0xe7, 0xe8, 0x88, 0xe2, 0x8e, 0xd1, 0x6f,
+       0x95, 0xb3, 0x56, 0xb7, 0xa0, 0xac, 0x62, 0x26,
+       0x57, 0x03, 0x69, 0x03, 0xf9, 0x5c, 0x8b, 0x1d,
+       0x6a, 0xd5, 0xab, 0xf9, 0x8f, 0x7a, 0x71, 0x51,
+       0xd6, 0x73, 0x22, 0x9a, 0xcd, 0x51, 0x7a, 0x72,
+       0x29, 0x3f, 0xd3, 0xfe, 0xfb, 0xbf, 0xf0, 0x74,
+       0x89, 0x09, 0xcb, 0xc9, 0xcd, 0x57, 0xbb, 0x4a,
+       0x83, 0x94, 0x01, 0xf1, 0x9e, 0x1f, 0x97, 0xe1,
+       0x50, 0x84, 0x5c, 0xd8, 0xb5, 0xb0, 0xe1, 0xab,
+       0xf1, 0x15, 0x19, 0x63, 0x29, 0x4f, 0x37, 0x3b,
+       0xa1, 0xec, 0x14, 0x40, 0xbf, 0xdb, 0x33, 0xbb,
+       0x46, 0xda, 0xf8, 0x3c, 0xa4, 0x73, 0x7e, 0xba,
+       0x97, 0x2a, 0x18, 0x57, 0x6b, 0xd6, 0xf8, 0x58};
+
+uint8_t tv_rrx[8] = {
+       0xe1, 0x7a, 0xb0, 0xfd, 0x0f, 0x54, 0x40, 0x52};
+
+uint8_t tv_hmac_rrx[32] = {
+       0xee, 0x6f, 0x40, 0x74, 0xeb, 0x1b, 0xd0, 0x7b,
+       0x35, 0x15, 0xb0, 0xf8, 0x28, 0x6a, 0xb5, 0x66,
+       0x96, 0xe9, 0x39, 0x2b, 0xd7, 0x62, 0xbe, 0xd4,
+       0x6a, 0x92, 0xd8, 0xd0, 0xa4, 0x18, 0x4d, 0x42};
+
+uint8_t tv_pairing_ekh[16] = {
+       0x2e, 0xec, 0xe5, 0x58, 0xe7, 0x8f, 0x1a, 0x96,
+       0xd3, 0xbd, 0x40, 0x14, 0xa6, 0x7e, 0x29, 0x3a};
+
+uint8_t tv_pairing_m[16] = {
+       0xf9, 0xf1, 0x30, 0xa8, 0x2d, 0x5b, 0xe5, 0xc3,
+       0xe1, 0x7a, 0xb0, 0xfd, 0x0f, 0x54, 0x40, 0x52};
+
+uint8_t tv_lc_hmac[32] = {
+       0x19, 0x16, 0xb1, 0x59, 0x73, 0xbe, 0xe3, 0x67,
+       0xf0, 0x56, 0x50, 0x51, 0x44, 0x0f, 0x53, 0xa2,
+       0xdf, 0x53, 0x8d, 0xce, 0xe2, 0x58, 0x1f, 0x65,
+       0xf3, 0xbf, 0x03, 0x0e, 0x68, 0x14, 0xe0, 0xe4};
+
+uint8_t tv_lc_lsb16_hmac[32] = {
+       0xdf, 0x53, 0x8d, 0xce, 0xe2, 0x58, 0x1f, 0x65,
+       0xf3, 0xbf, 0x03, 0x0e, 0x68, 0x14, 0xe0, 0xe4};
+
+uint8_t tv_eskey[16] = {
+       0xa5, 0xdb, 0xc5, 0x98, 0x50, 0xb1, 0xe4, 0x62,
+       0x09, 0x7d, 0x14, 0x49, 0xcb, 0x01, 0x44, 0x24};
+
+uint8_t tv_plain[32] = {
+       0x00, 0x00, 0x01, 0x00, 0x01, 0x1b, 0x3c, 0x5b,
+       0xb8, 0x00, 0x00, 0x00, 0x01, 0xb5, 0x85, 0x44,
+       0x3b, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb2,
+       0x44, 0x54, 0x47, 0x31, 0x41, 0xfe, 0x00, 0x00};
+
+uint8_t tv_cipher[32] = {
+       0x21, 0x8b, 0x92, 0xa3, 0x28, 0xc9, 0x84, 0x36,
+       0x17, 0x46, 0x4e, 0xd8, 0x68, 0x7a, 0x67, 0xb0,
+       0xeb, 0x66, 0x72, 0x30, 0x24, 0x5b, 0x70, 0x2a,
+       0x25, 0x86, 0xab, 0x4a, 0x7a, 0x9a, 0x3a, 0x4f};
+
+uint8_t tv_emkey_v22[128] = {
+       0xa8, 0x55, 0xc2, 0xc4, 0xc6, 0xbe, 0xef, 0xcd,
+       0xcb, 0x9f, 0xe3, 0x9f, 0x2a, 0xb7, 0x29, 0x76,
+       0xfe, 0xd8, 0xda, 0xc9, 0x38, 0xfa, 0x39, 0xf0,
+       0xab, 0xca, 0x8a, 0xed, 0x95, 0x7b, 0x93, 0xb2,
+       0xdf, 0xd0, 0x7d, 0x09, 0x9d, 0x05, 0x96, 0x66,
+       0x03, 0x6e, 0xba, 0xe0, 0x63, 0x0f, 0x30, 0x77,
+       0xc2, 0xbb, 0xe2, 0x11, 0x39, 0xe5, 0x27, 0x78,
+       0xee, 0x64, 0xf2, 0x85, 0x36, 0x57, 0xc3, 0x39,
+       0xd2, 0x7b, 0x79, 0x03, 0xb7, 0xcc, 0x82, 0xcb,
+       0xf0, 0x62, 0x82, 0x43, 0x38, 0x09, 0x9b, 0x71,
+       0xaa, 0x38, 0xa6, 0x3f, 0x48, 0x12, 0x6d, 0x8c,
+       0x5e, 0x07, 0x90, 0x76, 0xac, 0x90, 0x99, 0x51,
+       0x5b, 0x06, 0xa5, 0xfa, 0x50, 0xe4, 0xf9, 0x25,
+       0xc3, 0x07, 0x12, 0x37, 0x64, 0x92, 0xd7, 0xdb,
+       0xd3, 0x34, 0x1c, 0xe4, 0xfa, 0xdd, 0x09, 0xe6,
+       0x28, 0x3d, 0x0c, 0xad, 0xa9, 0xd8, 0xe1, 0xb5};
+
+uint8_t tv_pairing_ekh_v22[16] = {
+       0xe6, 0x57, 0x8e, 0xbc, 0xc7, 0x68, 0x44, 0x87,
+       0x88, 0x8a, 0x9b, 0xd7, 0xd6, 0xae, 0x38, 0xbe};
+
+uint8_t tv_pairing_m_v22[16] = {
+       0xf9, 0xf1, 0x30, 0xa8, 0x2d, 0x5b, 0xe5, 0xc3,
+       0xe1, 0x7a, 0xb0, 0xfd, 0x0f, 0x54, 0x40, 0x52};
+
+uint8_t tv_ske_eskey_v22[16] = {
+       0xb6, 0x8b, 0x8a, 0xa4, 0xd2, 0xcb, 0xba, 0xff,
+       0x53, 0x33, 0xc1, 0xd9, 0xbb, 0xb7, 0x10, 0xa9};
+
+#endif
+
+#endif
+
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.c b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.c
new file mode 100755 (executable)
index 0000000..3217ccd
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/smc.h>
+#include <asm/cacheflush.h>
+#include <linux/exynos_ion.h>
+#include <linux/smc.h>
+#if defined(CONFIG_ION)
+#include <linux/ion.h>
+#endif
+#include "iia_link/exynos-hdcp2-iia-auth.h"
+#include "exynos-hdcp2-teeif.h"
+#include "iia_link/exynos-hdcp2-iia-selftest.h"
+#include "exynos-hdcp2-encrypt.h"
+#include "exynos-hdcp2-log.h"
+#include "dp_link/exynos-hdcp2-dplink-if.h"
+#include "dp_link/exynos-hdcp2-dplink.h"
+#include "dp_link/exynos-hdcp2-dplink-selftest.h"
+
+#define EXYNOS_HDCP_DEV_NAME   "hdcp2"
+
+struct miscdevice hdcp;
+static DEFINE_MUTEX(hdcp_lock);
+struct hdcp_session_list g_hdcp_session_list;
+enum hdcp_result hdcp_link_ioc_authenticate(void);
+
+
+static uint32_t inst_num;
+
+static long hdcp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       int rval;
+
+       switch (cmd) {
+#if defined(CONFIG_HDCP2_IIA_ENABLE)
+       case (uint32_t)HDCP_IOC_SESSION_OPEN:
+       {
+               struct hdcp_sess_info ss_info;
+
+               rval = copy_from_user(&ss_info, (void __user *)arg, sizeof(struct hdcp_sess_info));
+               if (rval) {
+                       hdcp_err("Session open copy from user fail. (%x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               rval = hdcp_session_open(&ss_info);
+               if (rval) {
+                       hdcp_err("Session open fail. (%x)\n", rval);
+                       return rval;
+               }
+
+               rval = copy_to_user((void __user *)arg, &ss_info, sizeof(struct hdcp_sess_info));
+               if (rval) {
+                       hdcp_err("Session open copy to user fail. (%x)\n", rval);
+                       return HDCP_ERROR_COPY_TO_USER_FAILED;
+               }
+               break;
+       }
+       case (uint32_t)HDCP_IOC_SESSION_CLOSE:
+       {
+               /* todo: session close */
+               struct hdcp_sess_info ss_info;
+
+               rval = copy_from_user(&ss_info, (void __user *)arg, sizeof(struct hdcp_sess_info));
+               if (rval) {
+                       hdcp_err("Session close copy from user fail. (%x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               rval = hdcp_session_close(&ss_info);
+               if (rval) {
+                       hdcp_err("hdcp_session close fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_SESSION_CLOSE_FAILED;
+               }
+
+               break;
+       }
+       case (uint32_t)HDCP_IOC_LINK_OPEN:
+       {
+               struct hdcp_link_info lk_info;
+
+               rval = copy_from_user(&lk_info, (void __user *)arg, sizeof(struct hdcp_link_info));
+               if (rval) {
+                       hdcp_err("hdcp_link open copy from user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               rval = hdcp_link_open(&lk_info, HDCP_LINK_TYPE_IIA);
+               if (rval) {
+                       hdcp_err("hdcp_link open fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_LINK_OPEN_FAILED;
+               }
+
+               rval = copy_to_user((void __user *)arg, &lk_info, sizeof(struct hdcp_link_info));
+               if (rval) {
+                       hdcp_err("hdcp_link open copy to user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_TO_USER_FAILED;
+               }
+               break;
+       }
+       case (uint32_t)HDCP_IOC_LINK_CLOSE:
+       {
+               struct hdcp_link_info lk_info;
+
+               /* find Session node which contain the Link */
+               rval = copy_from_user(&lk_info, (void __user *)arg, sizeof(struct hdcp_link_info));
+               if (rval) {
+                       hdcp_err("hdcp_link close copy from user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               rval = hdcp_link_close(&lk_info);
+               if (rval) {
+                       hdcp_err("hdcp_link close fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_LINK_CLOSE_FAILED;
+               }
+
+               break;
+       }
+       case (uint32_t)HDCP_IOC_LINK_AUTH:
+       {
+               struct hdcp_msg_info msg_info;
+
+               rval = copy_from_user(&msg_info, (void __user *)arg, sizeof(struct hdcp_msg_info));
+               if (rval) {
+                       hdcp_err("hdcp_authenticate fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               rval = hdcp_link_authenticate(&msg_info);
+               if (rval) {
+                       hdcp_err("hdcp_authenticate fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_AUTHENTICATE_FAILED;
+               }
+
+               rval = copy_to_user((void __user *)arg, &msg_info, sizeof(struct hdcp_msg_info));
+               if (rval) {
+                       hdcp_err("hdcp_authenticate fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_TO_USER_FAILED;
+               }
+               break;
+       }
+       case (uint32_t)HDCP_IOC_LINK_ENC:
+       {
+               /* todo: link close */
+               struct hdcp_enc_info enc_info;
+               size_t packet_len = 0;
+               uint8_t pes_priv[HDCP_PRIVATE_DATA_LEN];
+               ion_phys_addr_t input_phys, output_phys;
+               struct hdcp_session_node *ss_node;
+               struct hdcp_link_node *lk_node;
+               struct hdcp_link_data *lk_data;
+               uint8_t *input_virt, *output_virt;
+               int ret = HDCP_SUCCESS;
+
+               /* find Session node which contain the Link */
+               rval = copy_from_user(&enc_info, (void __user *)arg, sizeof(struct hdcp_enc_info));
+               if (rval) {
+                       hdcp_err("hdcp_encrypt copy from user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               /* find Session node which contains the Link */
+               ss_node = hdcp_session_list_find(enc_info.id, &g_hdcp_session_list);
+               if (!ss_node)
+                       return HDCP_ERROR_INVALID_INPUT;
+
+               lk_node = hdcp_link_list_find(enc_info.id, &ss_node->ss_data->ln);
+               if (!lk_node)
+                       return HDCP_ERROR_INVALID_INPUT;
+
+               lk_data = lk_node->lk_data;
+
+               if (!lk_data)
+                       return HDCP_ERROR_INVALID_INPUT;
+
+               input_phys = (ion_phys_addr_t)enc_info.input_phys;
+               output_phys = (ion_phys_addr_t)enc_info.output_phys;
+
+               /* set input counters */
+               memset(&(lk_data->tx_ctx.input_ctr), 0x00, HDCP_INPUT_CTR_LEN);
+               /* set output counters */
+               memset(&(lk_data->tx_ctx.str_ctr), 0x00, HDCP_STR_CTR_LEN);
+
+               packet_len = (size_t)enc_info.input_len;
+               input_virt =  (uint8_t *)phys_to_virt(input_phys);
+               output_virt =  (uint8_t *)phys_to_virt(output_phys);
+
+               __flush_dcache_area(input_virt, packet_len);
+               __flush_dcache_area(output_virt, packet_len);
+
+               ret = encrypt_packet(pes_priv,
+                               input_phys, packet_len,
+                               output_phys, packet_len,
+                               &(lk_data->tx_ctx));
+
+               if (ret) {
+                       hdcp_err("encrypt_packet() is failed with 0x%x\n", ret);
+                       return -1;
+               }
+
+               rval = copy_to_user((void __user *)arg, &enc_info, sizeof(struct hdcp_enc_info));
+               if (rval) {
+                       hdcp_err("hdcp_encrypt copy to user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_TO_USER_FAILED;
+               }
+               break;
+       }
+       case (uint32_t)HDCP_IOC_STREAM_MANAGE:
+       {
+               struct hdcp_stream_info stream_info;
+
+               rval = copy_from_user(&stream_info, (void __user *)arg, sizeof(struct hdcp_stream_info));
+               if (rval) {
+                       hdcp_err("hdcp_link stream manage copy from user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               rval = hdcp_link_stream_manage(&stream_info);
+               if (rval) {
+                       hdcp_err("hdcp_link stream manage fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_LINK_STREAM_FAILED;
+               }
+
+               rval = copy_to_user((void __user *)arg, &stream_info, sizeof(struct hdcp_stream_info));
+               if (rval) {
+                       hdcp_err("hdcp_link stream manage copy to user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_TO_USER_FAILED;
+               }
+               break;
+       }
+#endif
+#if defined(CONFIG_HDCP2_EMULATION_MODE)
+       case (uint32_t)HDCP_IOC_EMUL_DPLINK_TX:
+       {
+               uint32_t emul_cmd;
+
+               if (copy_from_user(&emul_cmd, (void __user *)arg, sizeof(uint32_t)))
+                       return -EINVAL;
+
+               return dplink_emul_handler(emul_cmd);
+       }
+#endif
+       case (uint32_t)HDCP_IOC_DPLINK_TX_AUTH:
+       {
+#if defined(CONFIG_HDCP2_DP_ENABLE)
+               rval = dp_hdcp_protocol_self_test();
+               if (rval) {
+                       hdcp_err("DP self_test fail. errno(%d)\n", rval);
+                       return rval;
+               }
+               hdcp_err("DP self_test success!!\n");
+#endif
+#if defined(TEST_VECTOR_2)
+               /* todo: support test vector 1 */
+               rval = iia_hdcp_protocol_self_test();
+               if (rval) {
+                       hdcp_err("IIA self_test failed. errno(%d)\n", rval);
+                       return rval;
+               }
+               hdcp_err("IIA self_test success!!\n");
+#endif
+               rval = 0;
+               return rval;
+       }
+       case (uint32_t)HDCP_IOC_WRAP_KEY:
+       {
+               struct hdcp_wrapped_key key_info;
+
+               rval = copy_from_user(&key_info, (void __user *)arg, sizeof(struct hdcp_wrapped_key));
+               if (rval) {
+                       hdcp_err("hdcp_wrap_key copy from user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_FROM_USER_FAILED;
+               }
+
+               if (hdcp_wrap_key(&key_info))
+                       return HDCP_ERROR_WRAP_FAIL;
+
+               rval = copy_to_user((void __user *)arg, &key_info, sizeof(struct hdcp_wrapped_key));
+               if (rval) {
+                       hdcp_err("hdcp_wrap_key copy to user fail (0x%08x)\n", rval);
+                       return HDCP_ERROR_COPY_TO_USER_FAILED;
+               }
+               break;
+       }
+
+       default:
+               hdcp_err("HDCP: Invalid IOC num(%d)\n", cmd);
+               return -ENOTTY;
+       }
+
+       return 0;
+}
+
+static int hdcp_open(struct inode *inode, struct file *file)
+{
+       struct miscdevice *miscdev = file->private_data;
+       struct device *dev = miscdev->this_device;
+       struct hdcp_info *info;
+
+       info = kzalloc(sizeof(struct hdcp_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->dev = dev;
+       file->private_data = info;
+
+       mutex_lock(&hdcp_lock);
+       inst_num++;
+       /* todo: hdcp device initialize ? */
+       mutex_unlock(&hdcp_lock);
+
+       return 0;
+}
+
+static int hdcp_release(struct inode *inode, struct file *file)
+{
+       struct hdcp_info *info = file->private_data;
+
+       /* disable drm if we were the one to turn it on */
+       mutex_lock(&hdcp_lock);
+       inst_num--;
+       /* todo: hdcp device finalize ? */
+       mutex_unlock(&hdcp_lock);
+
+       kfree(info);
+       return 0;
+}
+
+static int __init hdcp_init(void)
+{
+       int ret;
+
+       hdcp_info("hdcp2 driver init\n");
+
+       ret = misc_register(&hdcp);
+       if (ret) {
+               hdcp_err("hdcp can't register misc on minor=%d\n",
+                               MISC_DYNAMIC_MINOR);
+               return ret;
+       }
+
+       /* todo: do initialize sequence */
+       hdcp_session_list_init(&g_hdcp_session_list);
+#if defined(CONFIG_HDCP2_DP_ENABLE)
+       if (hdcp_dplink_init() < 0) {
+               hdcp_err("hdcp_dplink_init fail\n");
+               return -EINVAL;
+       }
+#endif
+       ret = hdcp_tee_open();
+       if (ret) {
+               hdcp_err("hdcp_tee_open fail\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void __exit hdcp_exit(void)
+{
+       /* todo: do clear sequence */
+
+       misc_deregister(&hdcp);
+       hdcp_session_list_destroy(&g_hdcp_session_list);
+       hdcp_tee_close();
+}
+
+static const struct file_operations hdcp_fops = {
+       .owner          = THIS_MODULE,
+       .open           = hdcp_open,
+       .release        = hdcp_release,
+       .compat_ioctl = hdcp_ioctl,
+       .unlocked_ioctl = hdcp_ioctl,
+};
+
+struct miscdevice hdcp = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = EXYNOS_HDCP_DEV_NAME,
+       .fops   = &hdcp_fops,
+};
+
+module_init(hdcp_init);
+module_exit(hdcp_exit);
diff --git a/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.h b/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.h
new file mode 100755 (executable)
index 0000000..a68edb4
--- /dev/null
@@ -0,0 +1,228 @@
+/* driver/soc/samsung/exynos-hdcp/exynos-hdcp2.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_H__
+#define __EXYNOS_HDCP2_H__
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+
+#include "exynos-hdcp2-session.h"
+
+#define HDCP_CMD_SESSION_OPEN          0x0001
+#define HDCP_CMD_SESSION_CLOSE         0x0002
+#define HDCP_CMD_LINK_OPEN             0x0003
+#define HDCP_CMD_LINK_CLOSE            0x0004
+#define HDCP_CMD_LINK_AUTH             0x0005
+#define HDCP_CMD_LINK_ENC              0x0006
+
+/* todo: define max message length based on HDCP spec */
+#define HDCP_PROTO_MSG_LEN_MAX 1024
+
+/* HDCP Link types */
+#define HDCP_LINK_TYPE_IIA     0
+#define HDCP_LINK_TYPE_DP      1
+
+#ifndef TRUE
+#define TRUE   1
+#endif
+
+#ifndef FALSE
+#define FALSE  0
+#endif
+
+#define TEMP_ERROR                     -ENOTTY
+
+#define UPDATE_SESSION_STATE(sess, st)          do { \
+        printk("[HDCP2]HDCP Session(%d): %s -> %s\n", sess->id, hdcp_session_st_str[sess->state], hdcp_session_st_str[st]); \
+        sess->state = st; \
+        } while (0)
+
+#define UPDATE_LINK_STATE(link, st)             do { \
+        printk("[HDCP2]HDCP Link(%d): %s -> %s\n", link->id, hdcp_link_st_str[link->state], hdcp_link_st_str[st]); \
+        link->state = st; \
+        } while (0)
+
+#define UPDATE_STEP_STATE(step, st)          do { \
+        printk("[HDCP2]HDCP STEP(%d): %s -> %s\n", st, hdcp_msgid_str[step], hdcp_msgid_str[st]); \
+        step = st; \
+        } while (0)
+
+enum {
+       SEND_MSG                = 0,
+       RECIEVE_MSG             = 1,
+       RP_SEND_MSG             = 2,
+       RP_RECIEVE_MSG          = 3,
+       ST_SEND_MSG             = 4,
+       ST_RECIEVE_MSG          = 5,
+       DONE                    = 6,
+       AUTH_FINISHED           = 7,
+       RP_FINISHED             = 8,
+       WAIT_STATE              = 100
+};
+
+enum {
+       TX_AUTH_SUCCESS = 0,
+       TX_AUTH_ERROR_CONNECT_TEE,
+       TX_AUTH_ERROR_MAKE_PROTO_MSG,
+       TX_AUTH_ERROR_SEND_PROTO_MSG,
+       TX_AUTH_ERROR_RECV_PROTO_MSG,
+       TX_AUTH_ERROR_TIME_EXCEED,
+       TX_AUTH_ERROR_NET_TIMEOUT,
+       TX_AUTH_ERROR_NET_CONN_CLOSED,
+       TX_AUTH_ERROR_CRYPTO,
+       TX_AUTH_ERROR_WRONG_MSG_ID,
+       TX_AUTH_ERROR_WRONG_MSG,
+       TX_AUTH_ERROR_WRONG_MSG_SIZE,
+       TX_AUTH_ERROR_STORE_SKEY,
+       TX_AUTH_ERRRO_RESTORE_SKEY,
+       TX_AUTH_ERROR_BUFFER_OVERFLLOW,
+       TX_AUTH_ERROR_RETRYCOUNT_EXCEED,
+       TX_AUTH_ERROR_ABORT,
+};
+
+typedef enum hdcp_result {
+       HDCP_SUCCESS = 0,
+       HDCP_ERROR_INIT_FAILED,
+       HDCP_ERROR_TERMINATE_FAILED,
+       HDCP_ERROR_SESSION_OPEN_FAILED,
+       HDCP_ERROR_SESSION_CLOSE_FAILED,
+       HDCP_ERROR_LINK_OPEN_FAILED,
+       HDCP_ERROR_LINK_CLOSE_FAILED,
+       HDCP_ERROR_LINK_STREAM_FAILED,
+       HDCP_ERROR_AUTHENTICATE_FAILED,
+       HDCP_ERROR_COPY_TO_USER_FAILED,
+       HDCP_ERROR_COPY_FROM_USER_FAILED,
+       HDCP_ERROR_USER_FAILED,
+       HDCP_ERROR_RX_NOT_HDCP_CAPABLE,
+       HDCP_ERROR_EXCHANGE_KM,
+       HDCP_ERROR_LOCALITY_CHECK,
+       HDCP_ERROR_EXCHANGE_KS,
+       HDCP_ERROR_WAIT_RECEIVER_ID_LIST,
+       HDCP_ERROR_VERIFY_RECEIVER_ID_LIST,
+       HDCP_ERROR_WAIT_AKE_INIT,
+       HDCP_ERROR_MALLOC_FAILED = 0x1000,
+       HDCP_ERROR_INVALID_HANDLE,
+       HDCP_ERROR_INVALID_INPUT,
+       HDCP_ERROR_INVALID_STATE,
+       HDCP_ERROR_NET_UNREACHABLE,
+       HDCP_ERROR_NET_CLOSED,
+       HDCP_ERROR_ENCRYPTION,
+       HDCP_ERROR_DECRYPTION,
+       HDCP_ERROR_STREAM_MANAGE,
+       HDCP_ERROR_WRAP_FAIL,
+       HDCP_ERROR_UNWRAP_FAIL,
+       HDCP_ERROR_WRONG_SIZE,
+       HDCP_ERROR_DO_NOT_SUPPORT_YET,
+       HDCP_ERROR_IOCTL_OPEN,
+       HDCP_ERROR_END
+} hdcp_result;
+
+enum {
+       HDCP_ERROR_INVALID_SESSION_HANDLE = 0x2000,
+       HDCP_ERROR_INVALID_SESSION_STATE,
+       HDCP_ERROR_INVALID_STREAM_LEN,
+};
+
+struct hdcp_info {
+       struct device *dev;
+       struct hdcp_session_list ss_list;
+};
+
+struct hdcp_sess_info {
+       uint32_t ss_id;
+       uint8_t wkey[HDCP_WRAP_MAX_SIZE];
+};
+
+struct hdcp_link_info {
+       uint32_t ss_id;
+       uint32_t lk_id;
+};
+
+struct hdcp_link_auth {
+       uint32_t ss_id;
+       uint32_t lk_id;
+};
+
+struct hdcp_link_enc {
+       uint32_t ss_id;
+       uint32_t lk_id;
+};
+
+struct hdcp_msg_info {
+       uint32_t ss_handle;
+       uint32_t lk_id;
+       uint8_t msg[1024];
+       uint32_t msg_len;
+       uint32_t next_step;
+};
+
+struct hdcp_stream_info {
+       uint32_t ss_id;
+       uint32_t lk_id;
+       uint32_t num;
+       uint8_t type;
+       uint16_t stream_pid;
+       uint32_t stream_ctr;
+       uint8_t msg[1024];
+       uint32_t msg_len;
+       uint32_t next_step;
+};
+
+struct hdcp_enc_info {
+       uint32_t id;
+       uint8_t pes_private[16];
+       uint32_t priv_len;
+       uint8_t str_ctr[4];
+       uint8_t input_ctr[8];
+       uint64_t input;
+       unsigned long input_phys;
+       uint32_t input_len;
+       uint64_t output;
+       unsigned long output_phys;
+       uint32_t output_len;
+};
+
+struct hdcp_wrapped_key {
+       uint8_t key[16];
+       uint8_t enc_key[128];
+       uint32_t wrapped;
+       uint32_t key_len;
+};
+
+#define HDCP_IOC_SESSION_OPEN          _IOWR('S', 1, struct hdcp_sess_info)
+#define HDCP_IOC_SESSION_CLOSE         _IOWR('S', 2, struct hdcp_sess_info)
+#define HDCP_IOC_LINK_OPEN             _IOWR('S', 3, struct hdcp_link_info)
+#define HDCP_IOC_LINK_CLOSE            _IOWR('S', 4, struct hdcp_link_info)
+#define HDCP_IOC_LINK_AUTH             _IOWR('S', 5, struct hdcp_msg_info)
+#define HDCP_IOC_LINK_ENC              _IOWR('S', 6, struct hdcp_enc_info)
+#define HDCP_IOC_RESERVED_0            _IO('S', 7)
+#define HDCP_IOC_STREAM_MANAGE         _IOWR('S', 8, struct hdcp_stream_info)
+#define HDCP_IOC_IIA_TX_SELFTEST       _IOWR('S', 90, int)
+#define HDCP_IOC_DPLINK_TX_AUTH                _IOWR('S', 100, int)
+#define HDCP_IOC_DPLINK_TX_EMUL        _IOWR('S', 110, int)
+#define HDCP_IOC_DPLINK_TX_SELFTEST    _IOWR('S', 120, int)
+#define HDCP_IOC_WRAP_KEY              _IOWR('S', 200, struct hdcp_wrapped_key)
+
+typedef struct hdcp_tx_link_handle {
+        uint32_t ss_handle;
+        uint32_t lk_id;
+} hdcp_tx_link_handle_t;
+
+enum hdcp_result hdcp_session_open(struct hdcp_sess_info *ss_info);
+enum hdcp_result hdcp_session_close(struct hdcp_sess_info *ss_info);
+enum hdcp_result hdcp_link_open(struct hdcp_link_info *link_info, uint32_t lk_type);
+enum hdcp_result hdcp_link_close(struct hdcp_link_info *link_info);
+enum hdcp_result hdcp_link_authenticate(struct hdcp_msg_info *msg_info);
+enum hdcp_result hdcp_link_stream_manage(struct hdcp_stream_info *stream_info);
+enum hdcp_result hdcp_wrap_key(struct hdcp_wrapped_key *key_info);
+enum hdcp_result hdcp_unwrap_key(char *wkey);
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/hdcp2_if.h b/drivers/soc/samsung/exynos-hdcp/hdcp2_if.h
new file mode 100755 (executable)
index 0000000..049cf4a
--- /dev/null
@@ -0,0 +1,17 @@
+/* todo: include/soc/samsung/hdcp2-if.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __HDCP2_IF_H__
+#define __HDCP2_IF_H__
+
+int exynos_hdcp2_authenticate(uint32_t *lk_id);
+int exynos_hdcp2_encrypt(uint32_t link_id);
+
+#endif
+
diff --git a/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-auth.c b/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-auth.c
new file mode 100755 (executable)
index 0000000..ca7a596
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-tx-auth.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "exynos-hdcp2-iia-auth.h"
+#include "../exynos-hdcp2-log.h"
+
+#define HDCP_TX_VERSION_2_2    HDCP_TX_VERSION_2_1
+
+static int next_step;
+static int key_found;
+static char *hdcp_msgid_str[] = {
+       NULL,
+       "Null message",
+       "AKE_Init",
+       "AKE_Send_Cert",
+       "AKE_No_Stored_km",
+       "AKE_Stored_km",
+       "AKE_Send_rrx",
+       "AKE_Send_H_prime",
+       "AKE_Send_Pairing_Info",
+       "LC_Init",
+       "LC_Send_L_prime",
+       "SKE_Send_Eks",
+       "RepeaterAuth_Send_ReceiverID_List",
+       "RTT_Ready",
+       "RTT_Challenge",
+       "RepeaterAuth_Send_Ack",
+       "RepeaterAuth_Stream_Manage",
+       "RepeaterAuth_Stream_Ready",
+       "Receiver_AuthStatus",
+       "AKE_Transmitter_Info",
+       "AKE_Receiver_Info",
+       NULL
+};
+
+#define is_precomput(lk) \
+       ((lk->rx_ctx.version != HDCP_VERSION_2_0) && \
+       lk->tx_ctx.lc_precomp && \
+       lk->rx_ctx.lc_precomp)
+
+
+static int send_protocol_msg(struct hdcp_link_data *lk, uint8_t msg_id, struct hdcp_msg_info *msg_info)
+{
+        int ret = TX_AUTH_SUCCESS;
+
+       hdcp_info("Tx->Rx: %s\n", hdcp_msgid_str[msg_id]);
+       ret = cap_protocol_msg(msg_id, msg_info->msg, (size_t *)&msg_info->msg_len, lk->lk_type, &lk->tx_ctx, &lk->rx_ctx);
+
+        if (ret) {
+                hdcp_err("cap_protocol_msg() failed. ret(0x%08x)\n", ret);
+                return -TX_AUTH_ERROR_MAKE_PROTO_MSG;
+        }
+
+        return TX_AUTH_SUCCESS;
+}
+
+static int recv_protocol_msg(struct hdcp_link_data *lk, uint8_t msg_id, struct hdcp_msg_info *msg_info)
+{
+       int ret = TX_AUTH_SUCCESS;
+       if (ret >= 0 && msg_info->msg_len > 0) {
+               /* parsing received message */
+               ret = decap_protocol_msg(msg_id, msg_info->msg, msg_info->msg_len, lk->lk_type, &lk->tx_ctx, &lk->rx_ctx);
+
+               if (ret) {
+                       hdcp_err("dcap_protocol_msg() failed. msg_id(%d), ret(0x%08x)\n", msg_id, ret);
+                       ret = -TX_AUTH_ERROR_WRONG_MSG;
+               }
+       }
+
+       hdcp_info("Rx->Tx: %s\n", hdcp_msgid_str[msg_id]);
+       return ret;
+}
+
+static int hdcp_ake_find_masterkey(struct hdcp_tx_ctx *tx_ctx,
+               struct hdcp_rx_ctx *rx_ctx)
+{
+       /* todo */
+       return -1;
+}
+
+static int get_hdcp_session_key(struct hdcp_link_data *lk)
+{
+       /* todo */
+       return 0;
+}
+
+static int put_hdcp_session_key(struct hdcp_link_data *lk)
+{
+       /* todo */
+       return 0;
+}
+
+int exchange_master_key(struct hdcp_link_data *lk, struct hdcp_msg_info *msg_info)
+{
+       int rval = TX_AUTH_SUCCESS;
+
+       if (next_step < AKE_INIT)
+               UPDATE_STEP_STATE(next_step, AKE_INIT);
+
+       switch (next_step) {
+       case AKE_INIT:
+       {
+               rval = send_protocol_msg(lk, AKE_INIT, msg_info);
+               if (rval < 0) {
+                       hdcp_err("send AKE_Init failed. rval(%d)\n", rval);
+               } else {
+                       UPDATE_STEP_STATE(next_step, AKE_TRANSMITTER_INFO);
+#ifdef HDCP_TX_VERSION_2_2
+                       msg_info->next_step = SEND_MSG;
+#else
+                       msg_info->next_step = RECIEVE_MSG;
+#endif
+               }
+               break;
+       }
+       case AKE_TRANSMITTER_INFO:
+       {
+#ifdef HDCP_TX_VERSION_2_2
+               rval = send_protocol_msg(lk, AKE_TRANSMITTER_INFO, msg_info);
+               if (rval < 0) {
+                       hdcp_err("send AKE_Transmitter_Info failed. rval(%d)\n", rval);
+               } else {
+                       next_step++;
+                       UPDATE_STEP_STATE(next_step, AKE_SEND_CERT);
+                       msg_info->next_step = RECIEVE_MSG;
+               }
+               break;
+#endif
+       }
+       case AKE_SEND_CERT:
+       {
+               rval = recv_protocol_msg(lk, AKE_SEND_CERT, msg_info);
+               if (rval < 0) {
+                       hdcp_err("recv AKE_Send_Cert failed. rval(%d)\n", rval);
+               } else {
+                       UPDATE_STEP_STATE(next_step, AKE_RECEIVER_INFO);
+                       msg_info->next_step = RECIEVE_MSG;
+               }
+               break;
+       }
+       case AKE_RECEIVER_INFO:
+       {
+               rval = recv_protocol_msg(lk, AKE_RECEIVER_INFO, msg_info);
+               if (rval < 0) {
+                       hdcp_err("recv AKE_Receiver_Info failed. rval(%d)\n", rval);
+               } else {
+                       UPDATE_STEP_STATE(next_step, AKE_NO_STORED_KM);
+                       msg_info->next_step = SEND_MSG;
+               }
+               break;
+       }
+       case AKE_NO_STORED_KM:
+       {
+               key_found = hdcp_ake_find_masterkey(&lk->tx_ctx, &lk->rx_ctx);
+               if ((key_found < 0)) {
+                       rval = send_protocol_msg(lk, AKE_NO_STORED_KM, msg_info);
+                       if (rval < 0) {
+                               hdcp_err("send AKE_No_Stored_km failed. rval(%d)\n", rval);
+                       } else {
+                               UPDATE_STEP_STATE(next_step, AKE_SEND_RRX);
+                               msg_info->next_step = RECIEVE_MSG;
+                       }
+                       lk->stored_km = HDCP_WITHOUT_STORED_KM;
+               } else {
+                       rval = send_protocol_msg(lk, AKE_STORED_KM, msg_info);
+                       if (rval < 0) {
+                               hdcp_err("send AKE_Stored_km failed. rval(%d)\n", rval);
+                       } else {
+                               UPDATE_STEP_STATE(next_step, AKE_SEND_RRX);
+                               msg_info->next_step = RECIEVE_MSG;
+                       }
+                       lk->stored_km = HDCP_WITH_STORED_KM;
+               }
+               break;
+       }
+       case AKE_SEND_RRX:
+       {
+               rval = recv_protocol_msg(lk, AKE_SEND_RRX, msg_info);
+               if (rval < 0) {
+                       hdcp_err("recv AKE_Send_rrx failed. rval(%d)\n", rval);
+               } else {
+                       UPDATE_STEP_STATE(next_step, AKE_SEND_H_PRIME);
+                       msg_info->next_step = RECIEVE_MSG;
+               }
+               break;
+       }
+       case AKE_SEND_H_PRIME:
+       {
+               rval = recv_protocol_msg(lk, AKE_SEND_H_PRIME, msg_info);
+               if (rval < 0) {
+                       hdcp_err("recv AKE_Send_H_Prime failed. rval(%d)\n", rval);
+               } else {
+                       UPDATE_STEP_STATE(next_step, AKE_SEND_PAIRING_INFO);
+                       if(key_found == -1){
+                               hdcp_debug("Key found\n");
+                               msg_info->next_step = RECIEVE_MSG;
+                       }
+                       else {
+                               hdcp_debug("Key not found\n");
+                               msg_info->next_step = DONE;
+                       }
+               }
+               break;
+       }
+       case AKE_SEND_PAIRING_INFO:
+       {
+               if (key_found == -1) {
+                       rval = recv_protocol_msg(lk, AKE_SEND_PAIRING_INFO, msg_info);
+                       if (rval < 0) {
+                               hdcp_err("recv AKE_Send_Pairing_Info failed. rval(%d)\n", rval);
+                               break;
+                       }
+               }
+               msg_info->next_step = DONE;
+               UPDATE_STEP_STATE(next_step, LC_INIT);
+               break;
+       }
+       default:
+               return -ENOTTY;
+       }
+
+       if (rval != TX_AUTH_SUCCESS)
+               next_step = AKE_INIT;
+
+       return rval;
+}
+
+int locality_check(struct hdcp_link_data *lk, struct hdcp_msg_info *msg_info)
+{
+       int rval = TX_AUTH_SUCCESS;
+
+       switch (next_step) {
+       case LC_INIT:
+       {
+               rval = send_protocol_msg(lk, LC_INIT, msg_info);
+               if (rval < 0) {
+                       hdcp_err("send LC_Init failed. rval(%d)\n", rval);
+                       break;
+               }
+               else {
+                       UPDATE_STEP_STATE(next_step, RTT_READY);
+                       msg_info->next_step = RECIEVE_MSG;
+                       break;
+               }
+       }
+       case RTT_READY:
+       {
+               /* if no precompute */
+               if (!is_precomput(lk)) {
+                       hdcp_debug("LC with no precompute\n");
+                       rval = recv_protocol_msg(lk, LC_SEND_L_PRIME, msg_info);
+                       if (rval == TX_AUTH_SUCCESS) {
+                               UPDATE_STEP_STATE(next_step, AKE_INIT);
+                               msg_info->next_step = DONE;
+                       }
+                       else {
+                               hdcp_err("recv LC_Send_L_prime failed. rval(%d)\n", rval);
+                       }
+                       break;
+               } else {
+                       /* if precompute */
+                       rval = recv_protocol_msg(lk, RTT_READY, msg_info);
+                       if (rval < 0) {
+                               hdcp_err("recv RTT_Ready failed. rval(%d)\n", rval);
+                       }
+                       else {
+                               UPDATE_STEP_STATE(next_step, RTT_CHALLENGE);
+                               msg_info->next_step = SEND_MSG;
+                       }
+                       break;
+               }
+       }
+       case RTT_CHALLENGE:
+       {
+               rval = send_protocol_msg(lk, RTT_CHALLENGE, msg_info);
+               if (rval < 0) {
+                       hdcp_err("send RTT_Challenge failed. rval(%d)\n", rval);
+               }
+               else {
+                       UPDATE_STEP_STATE(next_step, LC_SEND_L_PRIME);
+                       msg_info->next_step = RECIEVE_MSG;
+               }
+               break;
+       }
+       case LC_SEND_L_PRIME:
+       {
+               rval = recv_protocol_msg(lk, LC_SEND_L_PRIME, msg_info);
+               if (rval == TX_AUTH_SUCCESS){
+                       msg_info->next_step = DONE;
+                       if (evaluate_repeater(lk))
+                               UPDATE_STEP_STATE(next_step, REPEATERAUTH_SEND_RECEIVERID_LIST);
+                       else
+                               UPDATE_STEP_STATE(next_step, AKE_INIT);
+               }
+               else {
+                       hdcp_err("recv LC_Send_L_prime failed. rval(%d)\n", rval);
+               }
+               break;
+       }
+       default:
+               return -ENOTTY;
+       }
+
+       return rval;
+}
+
+int exchange_hdcp_session_key(struct hdcp_link_data *lk, struct hdcp_msg_info *msg_info)
+{
+       int rval = TX_AUTH_SUCCESS;
+
+       /* find session key from the session data */
+       if (get_hdcp_session_key(lk) < 0) {
+               hdcp_err("get_hdcp_session_key() failed\n");
+               rval = -TX_AUTH_ERRRO_RESTORE_SKEY;
+       }
+
+       rval = send_protocol_msg(lk, SKE_SEND_EKS, msg_info);
+       if (rval < 0)
+               hdcp_err("send SKE_Send_Eks() failed\n");
+
+       if (put_hdcp_session_key(lk) < 0) {
+               hdcp_err("put_hdcp_session_key() failed\n");
+               rval = -TX_AUTH_ERROR_STORE_SKEY;
+       }
+
+       return rval;
+}
+
+int evaluate_repeater(struct hdcp_link_data *lk)
+{
+       if (lk->rx_ctx.repeater == REPEATER)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+int wait_for_receiver_id_list(struct hdcp_link_data *lk, struct hdcp_msg_info *msg_info)
+{
+        int rval = TX_AUTH_SUCCESS;
+
+        rval = recv_protocol_msg(lk, REPEATERAUTH_SEND_RECEIVERID_LIST, msg_info);
+        if (rval < 0) {
+                hdcp_err("recv RepeaterAuth_Send_ReceiverID_List failed. rval(%d)\n", rval);
+                return rval;
+        }
+
+       UPDATE_STEP_STATE(next_step, REPEATERAUTH_SEND_ACK);
+        return rval;
+}
+
+int send_receiver_id_list_ack(struct hdcp_link_data *lk, struct hdcp_msg_info *msg_info)
+{
+        int rval = TX_AUTH_SUCCESS;
+
+       switch (next_step) {
+       case REPEATERAUTH_SEND_ACK:
+       {
+               rval = send_protocol_msg(lk, REPEATERAUTH_SEND_ACK, msg_info);
+               if (rval < 0) {
+                       hdcp_err("send RepeaterAuth_Send_Ack() failed. rval(%d)\n", rval);
+               }
+               else {
+                       UPDATE_STEP_STATE(next_step, RECEIVER_AUTHSTATUS);
+                       msg_info->next_step = RP_RECIEVE_MSG;
+               }
+               break;
+       }
+       case RECEIVER_AUTHSTATUS:
+       {
+               rval = recv_protocol_msg(lk, RECEIVER_AUTHSTATUS, msg_info);
+               if (rval < 0) {
+                       hdcp_err("send Receiver_AuthStatus() failed. rval(%d)\n", rval);
+               }
+               else {
+                       UPDATE_STEP_STATE(next_step, REPEATERAUTH_STREAM_MANAGE);
+                       msg_info->next_step = DONE;
+               }
+               break;
+       }
+       default:
+               return -ENOTTY;
+       }
+
+        if (lk->tx_ctx.rp_reauth) {
+                hdcp_err("receiver request reauthentication\n");
+                return -TX_AUTH_ERROR_TIME_EXCEED;
+        }
+        else
+                hdcp_err("Repeater send reAuth value:: %d\n", lk->tx_ctx.rp_reauth);
+
+        return rval;
+}
+
+int manage_content_stream(struct hdcp_link_data *lk, struct contents_info *stream_ctrl, struct hdcp_stream_info *stream_info)
+{
+
+        int rval = TX_AUTH_SUCCESS;
+       struct hdcp_msg_info msg_info;
+
+       if (stream_info->msg_len < sizeof(msg_info.msg) && stream_info->msg_len > 0)
+               msg_info.msg_len = stream_info->msg_len;
+       else
+               return HDCP_ERROR_INVALID_STREAM_LEN;
+
+       memcpy(msg_info.msg, stream_info->msg, stream_info->msg_len);
+       switch (next_step) {
+       case REPEATERAUTH_STREAM_MANAGE:
+       {
+               memcpy(&lk->tx_ctx.stream_ctrl, stream_ctrl, sizeof(struct contents_info));
+               rval = send_protocol_msg(lk, REPEATERAUTH_STREAM_MANAGE, &msg_info);
+               if (rval < 0) {
+                       hdcp_err("send RepeaterAuth_stream_manage() failed. rval(%d)\n", rval);
+                       break;
+               }
+               else {
+                       UPDATE_STEP_STATE(next_step, REPEATERAUTH_STREAM_READY);
+                       stream_info->next_step = ST_RECIEVE_MSG;
+                       if (msg_info.msg_len > STREAM_MAX_LEN) {
+                               hdcp_err("send RepeaterAuth_stream_manage() failed. rval(%d)\n", rval);
+                               return TX_AUTH_ERROR_WRONG_MSG;
+                       }
+
+                       memcpy(stream_info->msg, msg_info.msg, msg_info.msg_len);
+                       stream_info->msg_len = msg_info.msg_len;
+                       break;
+               }
+
+       }
+       case REPEATERAUTH_STREAM_READY:
+       {
+               rval = recv_protocol_msg(lk, REPEATERAUTH_STREAM_READY, &msg_info);
+               if (rval < 0) {
+                       hdcp_err("send Receiver_AuthStatus() failed. rval(%d)\n", rval);
+                       break;
+               }
+               else {
+                       UPDATE_STEP_STATE(next_step, AKE_INIT);
+                       stream_info->next_step = RP_FINISHED;
+                       break;
+               }
+
+               if (msg_info.msg_len < sizeof(stream_info->msg) && msg_info.msg_len > 0)
+                       memcpy(stream_info->msg, msg_info.msg, msg_info.msg_len);
+       }
+       default:
+               return -ENOTTY;
+       }
+
+        return TX_AUTH_SUCCESS;
+}
+
+int determine_rx_hdcp_cap(struct hdcp_link_data *lk)
+{
+       /* todo */
+       return 0;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-auth.h b/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-auth.h
new file mode 100755 (executable)
index 0000000..b133989
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-tx-auth.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __HDCP_TX_AUTH_H__
+#define __HDCP_TX_AUTH_H__
+
+#include "../exynos-hdcp2.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ERR_WRONG_MESSAGE_LENGTH        0x1003
+#define ERR_WRONG_MESSAGE_ID            0x1004
+#define ERR_GENERATE_NON_SECKEY         0x1005
+#define ERR_FILE_OPEN                   0x1006
+
+int determine_rx_hdcp_cap(struct hdcp_link_data *);
+int exchange_master_key(struct hdcp_link_data *, struct hdcp_msg_info *);
+int locality_check(struct hdcp_link_data *, struct hdcp_msg_info *);
+int exchange_hdcp_session_key(struct hdcp_link_data *, struct hdcp_msg_info *);
+int evaluate_repeater(struct hdcp_link_data *);
+int wait_for_receiver_id_list(struct hdcp_link_data *, struct hdcp_msg_info *);
+int send_receiver_id_list_ack(struct hdcp_link_data *, struct hdcp_msg_info *);
+int manage_content_stream(struct hdcp_link_data *lk, struct contents_info *stream_ctrl, struct hdcp_stream_info *stream_info);
+int unwrapped_key(char *key, int wrapped);
+
+/* todo */
+int determine_rx_hdcp_cap(struct hdcp_link_data *lk);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-selftest.c b/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-selftest.c
new file mode 100644 (file)
index 0000000..385561b
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-selftest.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+
+#include "../exynos-hdcp2-config.h"
+#include "../exynos-hdcp2-protocol-msg.h"
+#include "../exynos-hdcp2-testvector.h"
+#include "../exynos-hdcp2-misc.h"
+#include "../exynos-hdcp2-encrypt.h"
+#include "../exynos-hdcp2.h"
+#include "../exynos-hdcp2-log.h"
+
+#define AKE_INIT_LEN                   9
+#define AKE_TRANSMITTER_INFO_LEN       6
+#define AKE_NO_STORED_KM_LEN           129
+#define AKE_STORED_KM_LEN              33
+#define AKE_SEND_PAIRING_INFO_LEN      17
+#define LC_INIT_LEN                    9
+#define LC_RTT_CHALLENGE_LEN           17
+#define SKE_SEND_EKS_LEN               25
+
+struct hdcp_tx_ctx uc_tx_ctx;
+struct hdcp_rx_ctx uc_rx_ctx;
+
+/* Test HDCP Encryption */
+static int utc_encryption(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t *input = NULL;
+       u64 input_phy = 0;
+       uint8_t *output = NULL;
+       u64 output_phy = 0;
+       int ret = 0;
+       size_t packet_len;
+       uint8_t pes_priv[HDCP_PRIVATE_DATA_LEN];
+
+       packet_len = sizeof(tv_plain);
+
+       /* Allocate enc in/out buffer for test */
+       input = (uint8_t *)kzalloc(packet_len, GFP_KERNEL);
+       if (!input) {
+               hdcp_err("alloc enc input buffer is failed\n");
+               return -ENOMEM;
+       }
+
+       output = (uint8_t *)kzalloc(packet_len, GFP_KERNEL);
+       if (!output) {
+               kfree(input);
+               hdcp_err("alloc enc input buffer is failed\n");
+               return -ENOMEM;
+       }
+
+       /* send physical address to SWd */
+       input_phy = virt_to_phys((void *)input);
+       output_phy = virt_to_phys((void *)output);
+
+       /* set input param */
+       memcpy(input, tv_plain, packet_len);
+       /* set input counters */
+       memset(&tx_ctx->input_ctr, 0x00, HDCP_INPUT_CTR_LEN);
+       /* set output counters */
+       memset(&tx_ctx->str_ctr, 0x00, HDCP_STR_CTR_LEN);
+
+       __flush_dcache_area(input, packet_len);
+       __flush_dcache_area(output, packet_len);
+       ret = encrypt_packet(pes_priv,
+               input_phy, packet_len,
+               output_phy, packet_len,
+               tx_ctx);
+       if (ret) {
+               kfree(input);
+               kfree(output);
+               hdcp_err("encrypt_packet() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (memcmp(output, tv_cipher, packet_len)) {
+               hdcp_err("Wrong encrypted value.\n");
+               hdcp_err("expected:\n");
+               hdcp_hexdump(tv_cipher, packet_len);
+
+               hdcp_err("actual:\n");
+               hdcp_hexdump(output, packet_len);
+
+               kfree(input);
+               kfree(output);
+               return -1;
+       } else
+               hdcp_info("HDCP:Encryption success.\n");
+
+       kfree(input);
+       kfree(output);
+       return 0;
+}
+
+static int utc_ske_send_eks(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       tx_ctx->share_skey = 0;
+
+       ret = cap_protocol_msg(SKE_SEND_EKS, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("SKE_Send_Eks() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* check message ID & length */
+       if (m[0] != 11) {
+               hdcp_err("Message ID ERROR\n");
+               return -1;
+       }
+
+       if (m_len != SKE_SEND_EKS_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+       /* compare encrypted session key with test vector */
+       for (i = 0; i < HDCP_AKE_MKEY_BYTE_LEN; i++)
+               if (tv_eskey[i] != m[1+i])
+                       break;
+
+       if (i != HDCP_AKE_MKEY_BYTE_LEN) {
+               hdcp_err("m doesn't match (%dth)\n", i);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Test SKE APIs */
+static int utc_ske(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx, int *cnt, int *fail)
+{
+       return utc_ske_send_eks(tx_ctx, rx_ctx);
+}
+
+static int utc_lc_send_l_prime(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(LC_SEND_L_PRIME, msg_rx_lc_send_l_prime,
+                       sizeof(msg_rx_send_h_prime),
+                       HDCP_LINK_TYPE_IIA,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("LC_Send_L_prime() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_rtt_challenge(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       ret = cap_protocol_msg(RTT_CHALLENGE, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("RTT_Challenge() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* check message ID & length */
+       if (m[0] != 14) {
+               hdcp_err("Message ID ERROR\n");
+               return -1;
+       }
+
+       if (m_len != LC_RTT_CHALLENGE_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+       /* check ls128_hmac */
+       for (i = 0; i < HDCP_HMAC_SHA256_LEN/2; i++)
+               if (m[i+1] != tv_lc_lsb16_hmac[i])
+                       break;
+
+       if (i != HDCP_HMAC_SHA256_LEN/2) {
+               hdcp_err("failed to compare ls128 hmac\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_rtt_ready(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(RTT_READY, msg_rx_rtt_ready,
+                       sizeof(msg_rx_rtt_ready),
+                       HDCP_LINK_TYPE_IIA,
+                       tx_ctx,
+                       rx_ctx);
+       if (ret) {
+               hdcp_err("RTT_Ready() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_lc_init(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       ret = cap_protocol_msg(LC_INIT, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("LC_Init() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* check message ID & length */
+       if (m[0] != 9) {
+               hdcp_err("Message ID ERROR\n");
+               return -1;
+       }
+
+       if (m_len != LC_INIT_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+       if ((rx_ctx->version != HDCP_VERSION_2_0)
+               && tx_ctx->lc_precomp
+               && rx_ctx->lc_precomp) {
+               for (i = 0; i < HDCP_HMAC_SHA256_LEN/2; i++)
+                       if (tx_ctx->lsb16_hmac[i] != tv_lc_hmac[i+16])
+                               break;
+               if (i != HDCP_HMAC_SHA256_LEN/2) {
+                       hdcp_err("failed to compare lsb16 hmac\n");
+                       return -1;
+               }
+       } else {
+               return 0;
+       }
+
+       return 0;
+}
+
+/* Test LC APIs */
+static int utc_lc(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx,
+               int *cnt, int *fail)
+{
+       int ret;
+
+       ret = utc_lc_init(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_lc_send_l_prime(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_rtt_ready(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_rtt_challenge(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int utc_ake_send_h_prime(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(AKE_SEND_H_PRIME, msg_rx_send_h_prime,
+                       sizeof(msg_rx_send_h_prime),
+                       HDCP_LINK_TYPE_IIA,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_Send_H_prime() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_send_rrx(struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       int i;
+
+       ret = decap_protocol_msg(AKE_SEND_RRX, msg_rx_send_rrx, sizeof(msg_rx_send_rrx),
+                               HDCP_LINK_TYPE_IIA, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("make_AKE_Send_rrx() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* compare rrx with test vector */
+       for (i = 0; i < HDCP_RRX_BYTE_LEN; i++)
+               if (rx_ctx->rrx[i] != tv_rrx[i])
+                       break;
+
+       if (i != HDCP_RRX_BYTE_LEN) {
+               hdcp_err("rrx doesn't match (%dth)\n", i);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_stored_km(struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+#if 0
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       ret = cap_protocol_msg(AKE_STORED_KM, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_Stored_km() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (m[0] != 5) {
+               hdcp_err("Message ID ERROR, it has %d\n", m[0]);
+               return -1;
+       }
+
+       if (m_len != AKE_STORED_KM_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+
+       for (i = 0; i < HDCP_AKE_MKEY_BYTE_LEN; i++)
+               if (tv_pairing_ekh[i] != m[i+1])
+                       break;
+
+       if (i != HDCP_AKE_MKEY_BYTE_LEN) {
+               hdcp_err("ekh(m) doesn't match (%dth)\n", i);
+               return -1;
+       }
+
+
+       for (i = 0; i < HDCP_AKE_MKEY_BYTE_LEN; i++)
+               if (tv_pairing_m[i] != m[i + 1 + HDCP_AKE_MKEY_BYTE_LEN])
+                       break;
+
+       if (i != HDCP_AKE_MKEY_BYTE_LEN) {
+               hdcp_err("m doesn't match (%dth)\n", i);
+               return -1;
+       }
+#endif
+
+       return 0;
+}
+
+static int utc_ake_send_pairing_info(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+       int i;
+       int found_km;
+
+       /* Extract Receiver ID */
+       for (i = 0; i < RECEIVER_ID_BYTE_LEN; i++)
+               rx_ctx->receiver_id[i] = rx_ctx->cert[i];
+
+       ret = decap_protocol_msg(AKE_SEND_PAIRING_INFO, msg_rx_send_pairing_info,
+                       sizeof(msg_rx_send_pairing_info),
+                       HDCP_LINK_TYPE_IIA,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("AKE_SEND_PAIRING_INFO() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       ret = ake_find_masterkey(&found_km,
+                               tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN,
+                               tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
+       if (ret) {
+               hdcp_err("find_masterkey() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (found_km) {
+               for (i = 0; i < HDCP_AKE_MKEY_BYTE_LEN; i++)
+                       if (tx_ctx->ekh_mkey[i] != msg_rx_send_pairing_info[i + 1])
+                               break;
+
+               if (i != HDCP_AKE_MKEY_BYTE_LEN) {
+                       hdcp_err("ekh(m) doesn't match (%dth)\n", i);
+                       return -1;
+               }
+       } else {
+               hdcp_err("ekh(m) is not found\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_no_stored_km(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+       int i;
+
+       memcpy(rx_ctx->cert, cert, HDCP_RX_CERT_LEN);
+
+       ret = cap_protocol_msg(AKE_NO_STORED_KM, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("make_AKE_No_Stored_km() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       if (m[0] != 4) {
+               hdcp_err("Message ID ERROR, it has %d\n", m[0]);
+               return -1;
+       }
+
+       if (m_len != AKE_NO_STORED_KM_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+
+       for (i = 0; i < 128; i++)
+               if (m[i + 1] != tv_emkey[i])
+                       break;
+
+       if (i != 128) {
+               hdcp_err("Encryption Master Key ERROR\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_receiver_info(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(AKE_RECEIVER_INFO,
+                               msg_rx_receiver_info,
+                               sizeof(msg_rx_receiver_info),
+                               HDCP_LINK_TYPE_IIA,
+                               tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("get_Receiver_Info() is failed with %x\n", ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_send_cert(struct hdcp_tx_ctx *tx_ctx,
+                       struct hdcp_rx_ctx *rx_ctx)
+{
+       int ret;
+
+       ret = decap_protocol_msg(AKE_SEND_CERT, msg_rx_send_cert,
+                       sizeof(msg_rx_send_cert),
+                       HDCP_LINK_TYPE_IIA,
+                       tx_ctx, rx_ctx);
+       if (ret) {
+               hdcp_err("get_AKE_Send_Cert() is failed with ret, 0x%x\n",
+               ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_transmitter_info(struct hdcp_tx_ctx *tx_ctx,
+                               struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+
+       ret = cap_protocol_msg(AKE_TRANSMITTER_INFO, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, NULL);
+       if (ret) {
+               hdcp_err("cap_ake_transmitter_info() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* check message ID & length */
+       if (m[0] != 19) {
+               hdcp_err("Message ID ERROR\n");
+               return -1;
+       }
+
+       if ((m_len != AKE_TRANSMITTER_INFO_LEN) || (m[2] < 6)) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+       /* Version & Precompuation Check */
+#ifdef HDCP_TX_VERSION_2_1
+       if (m[3] != HDCP_VERSION_2_1) {
+#else
+       if (m[3] == HDCP_VERSION_2_1) {
+#endif
+               hdcp_err("Version set wrong\n");
+               return -1;
+       }
+
+       /* precomputation check */
+#ifdef HDCP_TX_LC_PRECOMPUTE_SUPPORT
+       if ((m[4] != 0) || (m[5] != 0x01)) {
+#else
+       if ((m[4] != 0) || (m[5] != 0)) {
+#endif
+               hdcp_err("Precomputation ERROR, it has 0x%x%x\n", m[4], m[5]);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int utc_ake_init(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx)
+{
+       uint8_t m[525];
+       size_t m_len;
+       int ret;
+
+       ret = cap_protocol_msg(AKE_INIT, m, &m_len, HDCP_LINK_TYPE_IIA, tx_ctx, NULL);
+       if (ret) {
+               hdcp_err("cap_ake_init() is failed with 0x%x\n", ret);
+               return -1;
+       }
+
+       /* check message ID & length */
+       if (m[0] != 2) {
+               hdcp_err("Message ID ERROR\n");
+               return -1;
+       }
+
+       if (m_len != AKE_INIT_LEN) {
+               hdcp_err("Message LENGTH ERROR\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Test AKE APIs */
+static int utc_ake(struct hdcp_tx_ctx *tx_ctx, struct hdcp_rx_ctx *rx_ctx,
+               int *cnt, int *fail)
+{
+       int ret;
+
+       ret = utc_ake_init(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_transmitter_info(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_send_cert(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_receiver_info(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_no_stored_km(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_send_rrx(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_send_h_prime(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_send_pairing_info(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = utc_ake_stored_km(tx_ctx, rx_ctx);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* Test HDCP API functions */
+int iia_hdcp_protocol_self_test(void)
+{
+       int ake_cnt = 0, ake_fail = 0;
+       int lc_cnt = 0, lc_fail = 0;
+       int ske_cnt = 0, ske_fail = 0;
+       int ret;
+
+       hdcp_info("[ AKE UTC]\n");
+       ret = utc_ake(&uc_tx_ctx, &uc_rx_ctx, &ake_cnt, &ake_fail);
+       if (ret < 0) {
+               hdcp_info("AKE UTC: fail\n");
+               return ret;
+       } else {
+               hdcp_info("AKE UTC: success\n");
+       }
+
+       hdcp_info("\n[ LC UTC]\n");
+       ret = utc_lc(&uc_tx_ctx, &uc_rx_ctx, &lc_cnt, &lc_fail);
+       if (ret < 0) {
+               hdcp_info("LC UTC: fail\n");
+               return ret;
+       } else {
+               hdcp_info("LC UTC: success\n");
+       }
+
+       hdcp_info("\n[ SKE UTC]\n");
+       ret = utc_ske(&uc_tx_ctx, &uc_rx_ctx, &ske_cnt, &ske_fail);
+       if (ret < 0) {
+               hdcp_info("SKE UTC: fail\n");
+               return ret;
+       } else {
+               hdcp_info("SKE UTC: success\n");
+       }
+
+       hdcp_info("\n[ Encryption UTC]\n");
+       ret = utc_encryption(&uc_tx_ctx, &uc_rx_ctx);
+       if (ret < 0) {
+               hdcp_info("Encrypt UTC: fail\n");
+               return ret;
+       } else {
+               hdcp_info("Encrypt UTC: success\n");
+       }
+
+       return 0;
+}
diff --git a/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-selftest.h b/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia-selftest.h
new file mode 100755 (executable)
index 0000000..63b3ffa
--- /dev/null
@@ -0,0 +1,15 @@
+/* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-selftest.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef __EXYNOS_HDCP2_SELFTEST_H__
+#define __EXYNOS_HDCP2_SELFTEST_H__
+
+int iia_hdcp_protocol_self_test(void);
+
+#endif
diff --git a/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia.c b/drivers/soc/samsung/exynos-hdcp/iia_link/exynos-hdcp2-iia.c
new file mode 100755 (executable)
index 0000000..e15f71c
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * drivers/soc/samsung/exynos-hdcp/exynos-hdcp.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/smc.h>
+#include <asm/cacheflush.h>
+#include <linux/exynos_ion.h>
+#include <linux/smc.h>
+#if defined(CONFIG_ION)
+#include <linux/ion.h>
+#endif
+#include "exynos-hdcp2-iia-auth.h"
+#include "../exynos-hdcp2-teeif.h"
+#include "exynos-hdcp2-iia-selftest.h"
+#include "../exynos-hdcp2-encrypt.h"
+#include "../exynos-hdcp2-log.h"
+#include "../dp_link/exynos-hdcp2-dplink-if.h"
+#include "../dp_link/exynos-hdcp2-dplink.h"
+#include "../dp_link/exynos-hdcp2-dplink-selftest.h"
+
+#define EXYNOS_HDCP_DEV_NAME   "hdcp2"
+
+extern struct hdcp_session_list g_hdcp_session_list;
+enum hdcp_result hdcp_link_ioc_authenticate(void);
+static char *hdcp_session_st_str[] = {
+       "ST_INIT",
+       "ST_LINK_SETUP",
+       "ST_END",
+       NULL
+};
+
+static char *hdcp_link_st_str[] = {
+       "ST_INIT",
+       "ST_H0_NO_RX_ATTACHED",
+       "ST_H1_TX_LOW_VALUE_CONTENT",
+       "ST_A0_DETERMINE_RX_HDCP_CAP",
+       "ST_A1_EXCHANGE_MASTER_KEY",
+       "ST_A2_LOCALITY_CHECK",
+       "ST_A3_EXCHANGE_SESSION_KEY",
+       "ST_A4_TEST_REPEATER",
+       "ST_A5_AUTHENTICATED",
+       "ST_A6_WAIT_RECEIVER_ID_LIST",
+       "ST_A7_VERIFY_RECEIVER_ID_LIST",
+       "ST_A8_SEND_RECEIVER_ID_LIST_ACK",
+       "ST_A9_CONTENT_STREAM_MGT",
+       "ST_END",
+       NULL
+};
+
+int state_init_flag;
+
+enum hdcp_result hdcp_unwrap_key(char *wkey)
+{
+
+       int rval = TX_AUTH_SUCCESS;
+
+       rval = teei_wrapped_key(wkey, UNWRAP, HDCP_STATIC_KEY);
+       if (rval < 0) {
+               hdcp_err("Wrap(%d) key failed (0x%08x)\n", UNWRAP, rval);
+               return HDCP_ERROR_UNWRAP_FAIL;
+       }
+
+       return 0;
+}
+
+enum hdcp_result hdcp_session_open(struct hdcp_sess_info *ss_info)
+{
+       struct hdcp_session_data *new_ss = NULL;
+       struct hdcp_session_node *new_ss_node = NULL;
+
+       /* do open session */
+       new_ss_node = (struct hdcp_session_node *)kzalloc(sizeof(struct hdcp_session_node), GFP_KERNEL);
+
+       if (!new_ss_node) {
+               return HDCP_ERROR_INVALID_HANDLE;
+       }
+
+       new_ss = hdcp_session_data_create();
+       if (!new_ss) {
+               kfree(new_ss_node);
+               return HDCP_ERROR_INVALID_HANDLE;
+       }
+
+       /* send session info to SWD */
+       /* todo: add error check */
+
+       UPDATE_SESSION_STATE(new_ss, SESS_ST_LINK_SETUP);
+       ss_info->ss_id = new_ss->id;
+       new_ss_node->ss_data = new_ss;
+
+       hdcp_session_list_add((struct hdcp_session_node *)new_ss_node, (struct hdcp_session_list *)&g_hdcp_session_list);
+
+/* TODO: Only for IIA */
+#if 0
+       if (hdcp_unwrap_key(ss_info->wkey))
+               return HDCP_ERROR_WRAP_FAIL;
+#endif
+
+       return HDCP_SUCCESS;
+}
+
+enum hdcp_result hdcp_session_close(struct hdcp_sess_info *ss_info)
+{
+       struct hdcp_session_node *ss_node;
+       struct hdcp_session_data *ss_data;
+       uint32_t ss_handle;
+
+       ss_handle = ss_info->ss_id;
+
+       ss_node = hdcp_session_list_find(ss_handle, &g_hdcp_session_list);
+       if (!ss_node) {
+               return HDCP_ERROR_INVALID_HANDLE;
+       }
+
+       ss_data = ss_node->ss_data;
+       if (ss_data->state != SESS_ST_LINK_SETUP)
+               return HDCP_ERROR_INVALID_STATE;
+
+       ss_handle = ss_info->ss_id;
+       UPDATE_SESSION_STATE(ss_data, SESS_ST_END);
+
+       hdcp_session_list_del(ss_node, &g_hdcp_session_list);
+       hdcp_session_data_destroy(&(ss_node->ss_data));
+
+       return HDCP_SUCCESS;
+}
+
+enum hdcp_result hdcp_link_open(struct hdcp_link_info *link_info, uint32_t lk_type)
+{
+       struct hdcp_session_node *ss_node = NULL;
+       struct hdcp_link_node *new_lk_node = NULL;
+       struct hdcp_link_data *new_lk_data = NULL;
+       int ret = HDCP_SUCCESS;
+       uint32_t ss_handle;
+
+       ss_handle = link_info->ss_id;
+
+       do {
+               /* find Session node which will contain new Link */
+               ss_node = hdcp_session_list_find(ss_handle, &g_hdcp_session_list);
+               if (!ss_node) {
+                       ret = HDCP_ERROR_INVALID_INPUT;
+                       break;
+               }
+
+               /* make a new link node and add it to the session */
+               new_lk_node = (struct hdcp_link_node *)kzalloc(sizeof(struct hdcp_link_node), GFP_KERNEL);
+               if (!new_lk_node) {
+                       ret = HDCP_ERROR_MALLOC_FAILED;
+                       break;
+               }
+               new_lk_data = hdcp_link_data_create();
+               if (!new_lk_data) {
+                       ret = HDCP_ERROR_MALLOC_FAILED;
+                       break;
+               }
+
+               UPDATE_LINK_STATE(new_lk_data, LINK_ST_H0_NO_RX_ATTATCHED);
+
+               new_lk_data->ss_ptr = ss_node;
+               new_lk_data->lk_type = lk_type;
+               new_lk_node->lk_data = new_lk_data;
+
+               hdcp_link_list_add(new_lk_node, &ss_node->ss_data->ln);
+
+               link_info->ss_id = ss_node->ss_data->id;
+               link_info->lk_id = new_lk_data->id;
+       } while (0);
+
+       if (ret != HDCP_SUCCESS) {
+               if (new_lk_node)
+                       kfree(new_lk_node);
+               if (new_lk_data)
+                       hdcp_link_data_destroy(&new_lk_data);
+
+               return HDCP_ERROR_LINK_OPEN_FAILED;
+       }
+       else {
+               UPDATE_LINK_STATE(new_lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+       }
+
+       return HDCP_SUCCESS;
+}
+
+enum hdcp_result hdcp_link_close(struct hdcp_link_info *lk_info)
+{
+       struct hdcp_session_node *ss_node = NULL;
+       struct hdcp_link_node *lk_node = NULL;
+
+       /* find Session node which contain the Link */
+       ss_node = hdcp_session_list_find(lk_info->ss_id, &g_hdcp_session_list);
+
+       if (!ss_node)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       lk_node = hdcp_link_list_find(lk_info->lk_id, &ss_node->ss_data->ln);
+       if (!lk_node)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       UPDATE_LINK_STATE(lk_node->lk_data, LINK_ST_H0_NO_RX_ATTATCHED);
+
+       hdcp_link_list_del(lk_node, &ss_node->ss_data->ln);
+       hdcp_link_data_destroy(&(lk_node->lk_data));
+
+       return HDCP_SUCCESS;
+}
+
+enum hdcp_result hdcp_link_authenticate(struct hdcp_msg_info *msg_info)
+{
+       struct hdcp_session_node *ss_node;
+       struct hdcp_link_node *lk_node;
+       struct hdcp_link_data *lk_data;
+       int ret = HDCP_SUCCESS;
+       int rval = TX_AUTH_SUCCESS;
+       int ake_retry = 0;
+       int lc_retry = 0;
+
+       /* find Session node which contains the Link */
+       ss_node = hdcp_session_list_find(msg_info->ss_handle, &g_hdcp_session_list);
+       if (!ss_node)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       lk_node = hdcp_link_list_find(msg_info->lk_id, &ss_node->ss_data->ln);
+       if (!lk_node)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       lk_data = lk_node->lk_data;
+
+       if (!lk_data)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       /**
+        * if Upstream Content Control Function call this API,
+        * it changes state to ST_A0_DETERMINE_RX_HDCP_CAP automatically.
+        * HDCP library do not check CP desire.
+        */
+
+       if (state_init_flag == 0){
+               UPDATE_LINK_STATE(lk_data, LINK_ST_A0_DETERMINE_RX_HDCP_CAP);
+       }
+
+       if (lk_data->state == LINK_ST_A0_DETERMINE_RX_HDCP_CAP){
+               if (determine_rx_hdcp_cap(lk_data) < 0) {
+                       ret = HDCP_ERROR_RX_NOT_HDCP_CAPABLE;
+                       UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+               } else
+                       UPDATE_LINK_STATE(lk_data, LINK_ST_A1_EXCHANGE_MASTER_KEY);
+       }
+
+       switch (lk_data->state) {
+               case LINK_ST_H1_TX_LOW_VALUE_CONTENT:
+                       break;
+               case LINK_ST_A1_EXCHANGE_MASTER_KEY:
+                       rval = exchange_master_key(lk_data, msg_info);
+                       if (rval == TX_AUTH_SUCCESS) {
+                               if (msg_info->next_step == DONE) {
+                                       ake_retry = 0;
+                                       UPDATE_LINK_STATE(lk_data, LINK_ST_A2_LOCALITY_CHECK);
+                                       msg_info->next_step = SEND_MSG;
+                                       state_init_flag = 1;
+                               }
+                       } else {
+                               ret = HDCP_ERROR_EXCHANGE_KM;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       }
+                       break;
+               case LINK_ST_A2_LOCALITY_CHECK:
+                       rval = locality_check(lk_data, msg_info);
+                       if (rval == TX_AUTH_SUCCESS) {
+                               if (msg_info->next_step == DONE) {
+                                       lc_retry = 0;
+                                       UPDATE_LINK_STATE(lk_data, LINK_ST_A3_EXCHANGE_SESSION_KEY);
+                                       msg_info->next_step = SEND_MSG;
+                               }
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       }
+                       break;
+               case LINK_ST_A3_EXCHANGE_SESSION_KEY:
+                       if (exchange_hdcp_session_key(lk_data, msg_info) < 0) {
+                               ret = HDCP_ERROR_EXCHANGE_KS;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else{
+                               msg_info->next_step = WAIT_STATE;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A4_TEST_REPEATER);
+                       }
+                       break;
+               case LINK_ST_A4_TEST_REPEATER:
+                       if (evaluate_repeater(lk_data) == TRUE){
+                               /* HACK: when we supports repeater, it should be removed */
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A6_WAIT_RECEIVER_ID_LIST);
+                               msg_info->next_step = RP_RECIEVE_MSG;
+                       }
+                       else{
+                               /* if it is not a repeater, complete authentication */
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A5_AUTHENTICATED);
+                       }
+                       break;
+               case LINK_ST_A5_AUTHENTICATED:
+                       msg_info->next_step = AUTH_FINISHED;
+                       state_init_flag = 0;
+                       return HDCP_SUCCESS;
+               case LINK_ST_A6_WAIT_RECEIVER_ID_LIST:
+                       ret = wait_for_receiver_id_list(lk_data, msg_info);
+                       if (ret < 0) {
+                               ret = HDCP_ERROR_WAIT_RECEIVER_ID_LIST;
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_A7_VERIFY_RECEIVER_ID_LIST);
+                               msg_info->next_step = WAIT_STATE;
+                       }
+                       break;
+               case LINK_ST_A7_VERIFY_RECEIVER_ID_LIST:
+                       UPDATE_LINK_STATE(lk_data, LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK);
+                       msg_info->next_step = RP_SEND_MSG;
+                       break;
+               case LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK:
+                       rval = send_receiver_id_list_ack(lk_data, msg_info);
+                       if (rval == TX_AUTH_SUCCESS) {
+                               if (msg_info->next_step == DONE) {
+                                       UPDATE_LINK_STATE(lk_data, LINK_ST_A5_AUTHENTICATED);
+                                       state_init_flag = 0;
+                                       msg_info->next_step = AUTH_FINISHED;
+                               }
+                       } else {
+                               UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       }
+                       break;
+               case LINK_ST_A9_CONTENT_STREAM_MGT:
+                       /* do not support yet */
+                       ret = HDCP_ERROR_DO_NOT_SUPPORT_YET;
+                       break;
+               default:
+                       ret = HDCP_ERROR_INVALID_STATE;
+                       UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT);
+                       break;
+       }
+
+       return ret;
+}
+
+enum hdcp_result hdcp_link_stream_manage(struct hdcp_stream_info *stream_info)
+{
+       struct hdcp_session_node *ss_node;
+       struct hdcp_link_node *lk_node;
+       struct hdcp_link_data *lk_data;
+       struct contents_info stream_ctrl;
+       int rval = TX_AUTH_SUCCESS;
+       int i;
+
+       /* find Session node which contain the Link */
+       ss_node = hdcp_session_list_find(stream_info->ss_id, &g_hdcp_session_list);
+       if (!ss_node)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       lk_node = hdcp_link_list_find(stream_info->lk_id, &ss_node->ss_data->ln);
+       if (!lk_node)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       lk_data = lk_node->lk_data;
+       if (!lk_data)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       if (lk_data->state < LINK_ST_A4_TEST_REPEATER)
+               return HDCP_ERROR_INVALID_STATE;
+
+       stream_ctrl.str_num = stream_info->num;
+       if (stream_info->num > HDCP_TX_REPEATER_MAX_STREAM)
+               return HDCP_ERROR_INVALID_INPUT;
+
+       for (i = 0; i < stream_info->num; i++) {
+               stream_ctrl.str_info[i].ctr = stream_info->stream_ctr;
+               stream_ctrl.str_info[i].type = stream_info->type;
+               stream_ctrl.str_info[i].pid = stream_info->stream_pid;
+       }
+
+       rval = manage_content_stream(lk_data, &stream_ctrl, stream_info);
+       if (rval < 0) {
+               hdcp_err("manage_content_stream fail(0x%08x)\n", rval);
+               return HDCP_ERROR_STREAM_MANAGE;
+
+       }
+
+       return HDCP_SUCCESS;
+}
+
+enum hdcp_result hdcp_wrap_key(struct hdcp_wrapped_key *key_info)
+{
+       int rval = TX_AUTH_SUCCESS;
+       char key_str[HDCP_WRAP_MAX_SIZE];
+
+       if (key_info->key_len <= HDCP_WRAP_KEY)
+               memcpy(key_str, key_info->key, HDCP_WRAP_KEY);
+       else
+               return HDCP_ERROR_WRONG_SIZE;
+
+       rval = teei_wrapped_key(key_str, key_info->wrapped, key_info->key_len);
+       if (rval < 0) {
+               hdcp_err("Wrap(%d) key failed (0x%08x)\n",key_info->wrapped, rval);
+               return HDCP_ERROR_WRAP_FAIL;
+       }
+
+       if (key_info->wrapped == WRAP)
+               memcpy(key_info->enc_key, key_str, HDCP_WRAP_KEY + HDCP_WRAP_AUTH_TAG);
+
+       return 0;
+}