From b1ae6d832f8e339b3e2dd862d051028dd7179ec3 Mon Sep 17 00:00:00 2001 From: Phanindra Babu Pabba Date: Mon, 10 Feb 2020 23:07:04 +0530 Subject: [PATCH] Add hdcp_qseecom driver snapshot for Lahaina Add and modify files to enable hdcp_qseecom driver for Lahaina. This implementation is based on a snapshot of the HDCP QSEECOM module as of this commit c13c06fa4bc677f4 ("hdcp_qseecom: Maintain repeater_flag appropriately") on kernel-4.19. Change-Id: I91a9d9c539f5ab11af7f427e907622d8391b454f Signed-off-by: Phanindra Babu Pabba --- drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 + drivers/misc/hdcp_qseecom.c | 1762 +++++++++++++++++++++++++++++++++++ 3 files changed, 1773 insertions(+) create mode 100644 drivers/misc/hdcp_qseecom.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 34ab56f5149d..17c5f4d9fd1d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -489,6 +489,16 @@ config QSEECOM Manager (SCM) interface. It exposes APIs for both userspace and kernel clients. +config HDCP_QSEECOM + tristate "QTI High-Bandwidth Digital Content Protection Module" + depends on QSEECOM + help + This module implements HDCP 2.2 features over external interfaces + such as the DisplayPort interface. It exposes APIs for the interface + driver to communicate with QTI Secure Execution Environment (QSEE) + via the QSEECOM Driver and also communicates with the Receiver via + APIs exposed by the interface driver. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 59b616a0aa29..2da1e8fa1e16 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SRAM_EXEC) += sram-exec.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ +obj-$(CONFIG_HDCP_QSEECOM) += hdcp_qseecom.o obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o diff --git a/drivers/misc/hdcp_qseecom.c b/drivers/misc/hdcp_qseecom.c new file mode 100644 index 000000000000..c989edc2fe5e --- /dev/null +++ b/drivers/misc/hdcp_qseecom.c @@ -0,0 +1,1762 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "[hdcp-qseecom] %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qseecom_kernel.h" + +#define HDCP2P2_APP_NAME "hdcp2p2" +#define HDCP1_APP_NAME "hdcp1" +#define HDCPSRM_APP_NAME "hdcpsrm" +#define QSEECOM_SBUFF_SIZE 0x1000 + +#define MAX_TX_MESSAGE_SIZE 129 +#define MAX_RX_MESSAGE_SIZE 534 +#define MAX_TOPOLOGY_ELEMS 32 +#define HDCP1_AKSV_SIZE 8 + +#define HDCP1_SET_KEY 202 +#define HDCP1_KEY_VERIFY 204 +#define HDCP1_SET_ENC 205 + +#define BITS_8_IN_BYTES 1 +#define BITS_16_IN_BYTES 2 +#define BITS_24_IN_BYTES 3 +#define BITS_32_IN_BYTES 4 +#define BITS_40_IN_BYTES 5 +#define BITS_64_IN_BYTES 8 +#define BITS_128_IN_BYTES 16 +#define BITS_160_IN_BYTES 20 +#define BITS_256_IN_BYTES 32 +#define BITS_1024_IN_BYTES 128 +#define BITS_3072_IN_BYTES 384 +#define TXCAPS_SIZE 3 +#define RXCAPS_SIZE 3 +#define RXINFO_SIZE 2 +#define SEQ_NUM_V_SIZE 3 + +#define RCVR_ID_SIZE BITS_40_IN_BYTES +#define MAX_RCVR_IDS_ALLOWED_IN_LIST 31 +#define MAX_RCVR_ID_LIST_SIZE \ + (RCVR_ID_SIZE*MAX_RCVR_IDS_ALLOWED_IN_LIST) +/* + * minimum wait as per standard is 200 ms. keep it 300 ms + * to be on safe side. + */ +#define SLEEP_SET_HW_KEY_MS 300 + +/* Wait 200ms after authentication */ +#define SLEEP_FORCE_ENCRYPTION_MS 200 + +/* hdcp command status */ +#define HDCP_SUCCESS 0 + +const char *HdcpErrors[] = { + "HDCP_SUCCESS", + "HDCP_FAIL", + "HDCP_BAD_PARAM", + "HDCP_DEVICE_TYPE_UNSUPPORTED", + "HDCP_INVALID_COMMAND", + "HDCP_INVALID_COMMAND_HANDLE", + "HDCP_ERROR_SIZE_IN", + "HDCP_ERROR_SIZE_OUT", + "HDCP_DATA_SIZE_INSUFFICIENT", + "HDCP_UNSUPPORTED_RX_VERSION", + "HDCP_WRONG_RX_CAPAB_MASK", + "HDCP_WRONG_RX_RSVD", + "HDCP_WRONG_RX_HDCP_CAPABLE", + "HDCP_RSA_SIGNATURE_VERIFY_FAILED", + "HDCP_VERIFY_H_PRIME_FAILED", + "HDCP_LC_FAILED", + "HDCP_MESSAGE_TIMEOUT", + "HDCP_COUNTER_ROLL_OVER", + "HDCP_WRONG_RXINFO_RSVD", + "HDCP_RXINFO_MAX_DEVS", + "HDCP_RXINFO_MAX_CASCADE", + "HDCP_WRONG_INITIAL_SEQ_NUM_V", + "HDCP_SEQ_NUM_V_ROLL_OVER", + "HDCP_WRONG_SEQ_NUM_V", + "HDCP_VERIFY_V_FAILED", + "HDCP_RPT_METHOD_INVOKED", + "HDCP_RPT_STRM_LEN_WRONG", + "HDCP_VERIFY_STRM_M_FAILED", + "HDCP_TRANSMITTER_NOT_FOUND", + "HDCP_SESSION_NOT_FOUND", + "HDCP_MAX_SESSION_EXCEEDED", + "HDCP_MAX_CONNECTION_EXCEEDED", + "HDCP_MAX_STREAMS_EXCEEDED", + "HDCP_MAX_DEVICES", + "HDCP_ALLOC_FAILED", + "HDCP_CONNECTION_NOT_FOUND", + "HDCP_HASH_FAILED", + "HDCP_BN_FAILED", + "HDCP_ENCRYPT_KM_FAILED", + "HDCP_DECRYPT_KM_FAILED", + "HDCP_HMAC_FAILED", + "HDCP_GET_RANDOM_FAILED", + "HDCP_INVALID_KEY_HEADER", + "HDCP_INVALID_KEY_LC_HASH", + "HDCP_INVALID_KEY_HASH", + "HDCP_KEY_WRITE_FAILED", + "HDCP_KEY_READ_FAILED", + "HDCP_KEY_DECRYPT_FAILED", + "HDCP_TEST_KEY_ON_SECURE_DEVICE", + "HDCP_KEY_VERSION_UNSUPPORTED", + "HDCP_RXID_NOT_FOUND", + "HDCP_STORAGE_INIT_FAILED", + "HDCP_STORAGE_FILE_OPEN_FAILED", + "HDCP_STORAGE_FILE_READ_FAILED", + "HDCP_STORAGE_FILE_WRITE_FAILED", + "HDCP_STORAGE_ID_UNSUPPORTED", + "HDCP_MUTUAL_EXCLUSIVE_DEVICE_PRESENT", + "HDCP_INVALID_STATE", + "HDCP_CONFIG_READ_FAILED", + "HDCP_OPEN_TZ_SERVICE_FAILED", + "HDCP_HW_CLOCK_OFF", + "HDCP_SET_HW_KEY_FAILED", + "HDCP_CLEAR_HW_KEY_FAILED", + "HDCP_GET_CONTENT_LEVEL_FAILED", + "HDCP_STREAMID_INUSE", + "HDCP_STREAM_NOT_FOUND", + "HDCP_FORCE_ENCRYPTION_FAILED", + "HDCP_STREAMNUMBER_INUSE" +}; + +/* flags set by tz in response message */ +#define HDCP_TXMTR_SUBSTATE_INIT 0 +#define HDCP_TXMTR_SUBSTATE_WAITING_FOR_RECIEVERID_LIST 1 +#define HDCP_TXMTR_SUBSTATE_PROCESSED_RECIEVERID_LIST 2 +#define HDCP_TXMTR_SUBSTATE_WAITING_FOR_STREAM_READY_MESSAGE 3 +#define HDCP_TXMTR_SUBSTATE_REPEATER_AUTH_COMPLETE 4 + +#define HDCP_DEVICE_ID 0x0008000 +#define HDCP_CREATE_DEVICE_ID(x) (HDCP_DEVICE_ID | (x)) + +#define HDCP_TXMTR_HDMI HDCP_CREATE_DEVICE_ID(1) + +#define HDCP_TXMTR_SERVICE_ID 0x0001000 +#define SERVICE_CREATE_CMD(x) (HDCP_TXMTR_SERVICE_ID | x) + +#define HCDP_TXMTR_GET_MAJOR_VERSION(v) (((v) >> 16) & 0xFF) +#define HCDP_TXMTR_GET_MINOR_VERSION(v) (((v) >> 8) & 0xFF) +#define HCDP_TXMTR_GET_PATCH_VERSION(v) ((v) & 0xFF) + +#define HDCP_CLIENT_MAJOR_VERSION 2 +#define HDCP_CLIENT_MINOR_VERSION 1 +#define HDCP_CLIENT_PATCH_VERSION 0 +#define HDCP_CLIENT_MAKE_VERSION(maj, min, patch) \ + ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF)) + +#define hdcp2_app_init_var(x) \ + struct hdcp_##x##_req *req_buf = NULL; \ + struct hdcp_##x##_rsp *rsp_buf = NULL; \ + if (!handle->qseecom_handle) { \ + pr_err("invalid qseecom_handle while processing %s\n", #x); \ + rc = -EINVAL; \ + goto error; \ + } \ + req_buf = (struct hdcp_##x##_req *) handle->qseecom_handle->sbuf; \ + rsp_buf = (struct hdcp_##x##_rsp *) (handle->qseecom_handle->sbuf + \ + QSEECOM_ALIGN(sizeof(struct hdcp_##x##_req))); \ + req_buf->commandid = hdcp_cmd_##x + + +#define hdcp2_app_process_cmd(x) \ +({ \ + int rc = qseecom_send_command(handle->qseecom_handle, \ + req_buf, QSEECOM_ALIGN(sizeof(struct hdcp_##x##_req)), \ + rsp_buf, QSEECOM_ALIGN(sizeof(struct hdcp_##x##_rsp))); \ + if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS)) { \ + pr_err("qseecom cmd %s failed with err = %d, status = %d:%s\n" \ + , #x, rc, rsp_buf->status, \ + hdcp_cmd_status_to_str(rsp_buf->status)); \ + rc = -EINVAL; \ + } \ + rc; \ +}) + +enum { + hdcp_cmd_tx_init = SERVICE_CREATE_CMD(1), + hdcp_cmd_tx_init_v1 = SERVICE_CREATE_CMD(1), + hdcp_cmd_tx_deinit = SERVICE_CREATE_CMD(2), + hdcp_cmd_rcvd_msg = SERVICE_CREATE_CMD(3), + hdcp_cmd_send_timeout = SERVICE_CREATE_CMD(4), + hdcp_cmd_set_hw_key = SERVICE_CREATE_CMD(5), + hdcp_cmd_query_stream_type = SERVICE_CREATE_CMD(6), + hdcp_cmd_init_v1 = SERVICE_CREATE_CMD(11), + hdcp_cmd_init = SERVICE_CREATE_CMD(11), + hdcp_cmd_deinit = SERVICE_CREATE_CMD(12), + hdcp_cmd_version = SERVICE_CREATE_CMD(14), + hdcp_cmd_verify_key = SERVICE_CREATE_CMD(15), + hdcp_cmd_session_init = SERVICE_CREATE_CMD(16), + hdcp_cmd_session_deinit = SERVICE_CREATE_CMD(17), + hdcp_cmd_start_auth = SERVICE_CREATE_CMD(18), + hdcp_cmd_session_open_stream = SERVICE_CREATE_CMD(20), + hdcp_cmd_session_close_stream = SERVICE_CREATE_CMD(21), + hdcp_cmd_force_encryption = SERVICE_CREATE_CMD(22), +}; + +enum hdcp_state { + HDCP_STATE_INIT = 0x00, + HDCP_STATE_APP_LOADED = 0x01, + HDCP_STATE_SESSION_INIT = 0x02, + HDCP_STATE_TXMTR_INIT = 0x04, + HDCP_STATE_AUTHENTICATED = 0x08, + HDCP_STATE_ERROR = 0x10 +}; + +enum hdcp_element { + HDCP_TYPE_UNKNOWN, + HDCP_TYPE_RECEIVER, + HDCP_TYPE_REPEATER, +}; + +enum hdcp_version { + HDCP_VERSION_UNKNOWN, + HDCP_VERSION_2_2, + HDCP_VERSION_1_4 +}; + +struct receiver_info { + unsigned char rcvrInfo[RCVR_ID_SIZE]; + enum hdcp_element elem_type; + enum hdcp_version hdcp_version; +}; + +struct topology_info { + unsigned int nNumRcvrs; + struct receiver_info rcvinfo[MAX_TOPOLOGY_ELEMS]; +}; + +struct __attribute__ ((__packed__)) hdcp1_key_set_req { + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp1_key_set_rsp { + uint32_t commandid; + uint32_t ret; + uint8_t ksv[HDCP1_AKSV_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_version_req { + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_version_rsp { + uint32_t status; + uint32_t commandId; + uint32_t appversion; +}; + +struct __attribute__ ((__packed__)) hdcp_verify_key_req { + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_verify_key_rsp { + uint32_t status; + uint32_t commandId; +}; + +struct __attribute__ ((__packed__)) hdcp1_key_verify_req { + uint32_t commandid; + uint32_t key_type; +}; + +struct __attribute__ ((__packed__)) hdcp1_key_verify_rsp { + uint32_t commandId; + uint32_t ret; +}; + +struct __attribute__ ((__packed__)) hdcp_init_v1_req { + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_init_v1_rsp { + uint32_t status; + uint32_t commandid; + uint32_t ctxhandle; + uint32_t timeout; + uint32_t msglen; + uint8_t message[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_init_req { + uint32_t commandid; + uint32_t clientversion; +}; + +struct __attribute__ ((__packed__)) hdcp_init_rsp { + uint32_t status; + uint32_t commandid; + uint32_t appversion; +}; + +struct __attribute__ ((__packed__)) hdcp_deinit_req { + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_deinit_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_session_init_req { + uint32_t commandid; + uint32_t deviceid; +}; + +struct __attribute__ ((__packed__)) hdcp_session_init_rsp { + uint32_t status; + uint32_t commandid; + uint32_t sessionid; +}; + +struct __attribute__ ((__packed__)) hdcp_session_deinit_req { + uint32_t commandid; + uint32_t sessionid; +}; + +struct __attribute__ ((__packed__)) hdcp_session_deinit_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_tx_init_v1_req { + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_tx_init_v1_rsp { + uint32_t status; + uint32_t commandid; + uint32_t ctxhandle; + uint32_t timeout; + uint32_t msglen; + uint8_t message[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_tx_init_req { + uint32_t commandid; + uint32_t sessionid; +}; + +struct __attribute__ ((__packed__)) hdcp_tx_init_rsp { + uint32_t status; + uint32_t commandid; + uint32_t ctxhandle; +}; + +struct __attribute__ ((__packed__)) hdcp_tx_deinit_req { + uint32_t commandid; + uint32_t ctxhandle; +}; + +struct __attribute__ ((__packed__)) hdcp_tx_deinit_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_rcvd_msg_req { + uint32_t commandid; + uint32_t ctxhandle; + uint32_t msglen; + uint8_t msg[MAX_RX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_rcvd_msg_rsp { + uint32_t status; + uint32_t commandid; + uint32_t state; + uint32_t timeout; + uint32_t flag; + uint32_t msglen; + uint8_t msg[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_set_hw_key_req { + uint32_t commandid; + uint32_t ctxhandle; +}; + +struct __attribute__ ((__packed__)) hdcp_set_hw_key_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_send_timeout_req { + uint32_t commandid; + uint32_t ctxhandle; +}; + +struct __attribute__ ((__packed__)) hdcp_send_timeout_rsp { + uint32_t status; + uint32_t commandid; + uint32_t timeout; + uint32_t msglen; + uint8_t message[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_query_stream_type_req { + uint32_t commandid; + uint32_t ctxhandle; +}; + +struct __attribute__ ((__packed__)) hdcp_query_stream_type_rsp { + uint32_t status; + uint32_t commandid; + uint32_t timeout; + uint32_t msglen; + uint8_t msg[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_set_stream_type_req { + uint32_t commandid; + uint32_t ctxhandle; + uint8_t streamtype; +}; + +struct __attribute__ ((__packed__)) hdcp_set_stream_type_rsp { + uint32_t status; + uint32_t commandid; + uint32_t timeout; + uint32_t msglen; + uint8_t message[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__ ((__packed__)) hdcp_update_srm_req { + uint32_t commandid; + uint32_t ctxhandle; + uint32_t srmoffset; + uint32_t srmlength; +}; + +struct __attribute__ ((__packed__)) hdcp_update_srm_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct __attribute__ ((__packed__)) hdcp_get_topology_req { + uint32_t commandid; + uint32_t ctxhandle; +}; + +struct __attribute__ ((__packed__)) hdcp_get_topology_rsp { + uint32_t status; + uint32_t commandid; + struct topology_info topologyinfo; +}; + +struct __attribute__ ((__packed__)) rxvr_info_struct { + uint8_t rcvrCert[522]; + uint8_t rrx[BITS_64_IN_BYTES]; + uint8_t rxcaps[RXCAPS_SIZE]; + bool repeater; +}; + +struct __attribute__ ((__packed__)) repeater_info_struct { + uint8_t RxInfo[RXINFO_SIZE]; + uint8_t seq_num_V[SEQ_NUM_V_SIZE]; + bool seq_num_V_Rollover_flag; + uint8_t ReceiverIDList[MAX_RCVR_ID_LIST_SIZE]; + uint32_t ReceiverIDListLen; +}; + +struct __attribute__ ((__packed__)) hdcp1_set_enc_req { + uint32_t commandid; + uint32_t enable; +}; + +struct __attribute__ ((__packed__)) hdcp1_set_enc_rsp { + uint32_t commandid; + uint32_t ret; +}; + +struct __attribute__ ((__packed__)) hdcp_start_auth_req { + uint32_t commandid; + uint32_t ctxHandle; +}; + +struct __attribute__ ((__packed__)) hdcp_start_auth_rsp { + uint32_t status; + uint32_t commandid; + uint32_t ctxhandle; + uint32_t timeout; + uint32_t msglen; + uint8_t message[MAX_TX_MESSAGE_SIZE]; +}; + +struct __attribute__((__packed__)) hdcp_session_open_stream_req { + uint32_t commandid; + uint32_t sessionid; + uint32_t vcpayloadid; + uint32_t stream_number; + uint32_t streamMediaType; +}; + +struct __attribute__((__packed__)) hdcp_session_open_stream_rsp { + uint32_t status; + uint32_t commandid; + uint32_t streamid; +}; + +struct __attribute__((__packed__)) hdcp_session_close_stream_req { + uint32_t commandid; + uint32_t sessionid; + uint32_t streamid; +}; + +struct __attribute__((__packed__)) hdcp_session_close_stream_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct __attribute__((__packed__)) hdcp_force_encryption_req { + uint32_t commandid; + uint32_t ctxhandle; + uint32_t enable; +}; + +struct __attribute__ ((__packed__)) hdcp_force_encryption_rsp { + uint32_t status; + uint32_t commandid; +}; + +struct hdcp2_handle { + struct hdcp2_app_data app_data; + uint32_t tz_ctxhandle; + bool feature_supported; + enum hdcp_state hdcp_state; + struct qseecom_handle *qseecom_handle; + struct qseecom_handle *hdcpsrm_qseecom_handle; + uint32_t session_id; + bool legacy_app; + uint32_t device_type; + char *app_name; + + int (*app_init)(struct hdcp2_handle *handle); + int (*tx_init)(struct hdcp2_handle *handle); +}; + +/* + * struct hdcp1_handle - handle for HDCP 1.x client + * @qseecom_handle - for sending commands to qseecom + * @feature_supported - set to true if the platform supports HDCP 1.x + * @device_type - the interface type (HDMI or DisplayPort) + */ +struct hdcp1_handle { + struct qseecom_handle *qseecom_handle; + bool feature_supported; + uint32_t device_type; + enum hdcp_state hdcp_state; + char *app_name; +}; + +#define HDCP_CMD_STATUS_TO_STR(x) #x +static const char *hdcp_cmd_status_to_str(uint32_t err) +{ + int len = ARRAY_SIZE(HdcpErrors); + + if (err >= 0 && err < len) + return HdcpErrors[err]; + else + return ""; +} + +static int hdcp_get_version(struct hdcp2_handle *handle) +{ + int rc = 0; + uint32_t app_major_version = 0; + + hdcp2_app_init_var(version); + + if (handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_err("library already loaded\n"); + goto error; + } + + rc = hdcp2_app_process_cmd(version); + if (rc) + goto error; + + app_major_version = HCDP_TXMTR_GET_MAJOR_VERSION(rsp_buf->appversion); + + pr_debug("hdp2p2 app major version %d, app version %d\n", + app_major_version, rsp_buf->appversion); + + if (app_major_version == 1) + handle->legacy_app = true; +error: + return rc; +} + +static int hdcp2_app_init_legacy(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(init_v1); + + if (!handle->legacy_app) { + pr_err("wrong init function\n"); + rc = -EINVAL; + goto error; + } + + if (handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_err("library already loaded\n"); + goto error; + } + + rc = hdcp2_app_process_cmd(init_v1); + if (rc) + goto error; + + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_init(struct hdcp2_handle *handle) +{ + int rc = 0; + uint32_t app_minor_version = 0; + + hdcp2_app_init_var(init); + + if (handle->legacy_app) { + pr_err("wrong init function\n"); + rc = -EINVAL; + goto error; + } + + if (handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_err("library already loaded\n"); + goto error; + } + + req_buf->clientversion = + HDCP_CLIENT_MAKE_VERSION(HDCP_CLIENT_MAJOR_VERSION, + HDCP_CLIENT_MINOR_VERSION, + HDCP_CLIENT_PATCH_VERSION); + + rc = hdcp2_app_process_cmd(init); + if (rc) + goto error; + + app_minor_version = HCDP_TXMTR_GET_MINOR_VERSION(rsp_buf->appversion); + if (app_minor_version != HDCP_CLIENT_MINOR_VERSION) { + pr_err + ("client-app minor version mismatch app(%d), client(%d)\n", + app_minor_version, HDCP_CLIENT_MINOR_VERSION); + rc = -1; + goto error; + } + + pr_debug("success\n"); + + pr_debug("client version major(%d), minor(%d), patch(%d)\n", + HDCP_CLIENT_MAJOR_VERSION, HDCP_CLIENT_MINOR_VERSION, + HDCP_CLIENT_PATCH_VERSION); + + pr_debug("app version major(%d), minor(%d), patch(%d)\n", + HCDP_TXMTR_GET_MAJOR_VERSION(rsp_buf->appversion), + HCDP_TXMTR_GET_MINOR_VERSION(rsp_buf->appversion), + HCDP_TXMTR_GET_PATCH_VERSION(rsp_buf->appversion)); +error: + return rc; +} + +static int hdcp2_app_tx_init(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(tx_init); + + if (!(handle->hdcp_state & HDCP_STATE_SESSION_INIT)) { + pr_err("session not initialized\n"); + rc = -EINVAL; + goto error; + } + + if (handle->hdcp_state & HDCP_STATE_TXMTR_INIT) { + pr_err("txmtr already initialized\n"); + goto error; + } + + req_buf->sessionid = handle->session_id; + + rc = hdcp2_app_process_cmd(tx_init); + if (rc) + goto error; + + handle->tz_ctxhandle = rsp_buf->ctxhandle; + handle->hdcp_state |= HDCP_STATE_TXMTR_INIT; + + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_tx_init_legacy(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(tx_init_v1); + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("app not loaded\n"); + rc = -EINVAL; + goto error; + } + + if (handle->hdcp_state & HDCP_STATE_TXMTR_INIT) { + pr_err("txmtr already initialized\n"); + goto error; + } + + rc = hdcp2_app_process_cmd(tx_init_v1); + if (rc) + goto error; + + handle->app_data.response.data = rsp_buf->message; + handle->app_data.response.length = rsp_buf->msglen; + handle->app_data.timeout = rsp_buf->timeout; + + handle->tz_ctxhandle = rsp_buf->ctxhandle; + handle->hdcp_state |= HDCP_STATE_TXMTR_INIT; + + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_load(struct hdcp2_handle *handle) +{ + int rc = 0; + + if (!handle) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto error; + } + + if (handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_err("%s app already loaded\n", handle->app_name); + goto error; + } + + rc = qseecom_start_app(&handle->qseecom_handle, + handle->app_name, QSEECOM_SBUFF_SIZE); + if (rc) { + pr_err("qseecom_start_app failed for HDCP2P2 (%d)\n", rc); + goto error; + } + + rc = qseecom_start_app(&handle->hdcpsrm_qseecom_handle, + HDCPSRM_APP_NAME, QSEECOM_SBUFF_SIZE); + if (rc) { + pr_err("qseecom_start_app failed for HDCPSRM (%d)\n", rc); + goto hdcpsrm_error; + } + + pr_debug("qseecom_start_app success\n"); + + rc = hdcp_get_version(handle); + if (rc) { + pr_err("library get version failed\n"); + goto get_version_error; + } + + if (handle->legacy_app) { + handle->app_init = hdcp2_app_init_legacy; + handle->tx_init = hdcp2_app_tx_init_legacy; + } else { + handle->app_init = hdcp2_app_init; + handle->tx_init = hdcp2_app_tx_init; + } + + rc = handle->app_init(handle); + if (rc) { + pr_err("app init failed\n"); + goto get_version_error; + } + + handle->hdcp_state |= HDCP_STATE_APP_LOADED; + + return rc; +get_version_error: + qseecom_shutdown_app(&handle->hdcpsrm_qseecom_handle); +hdcpsrm_error: + qseecom_shutdown_app(&handle->qseecom_handle); +error: + return rc; +} + +static int hdcp2_app_unload(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(deinit); + + hdcp2_app_process_cmd(deinit); + + /* deallocate the resources for qseecom HDCPSRM handle */ + rc = qseecom_shutdown_app(&handle->hdcpsrm_qseecom_handle); + if (rc) + pr_err("qseecom_shutdown_app failed for HDCPSRM (%d)\n", rc); + + /* deallocate the resources for qseecom HDCP2P2 handle */ + rc = qseecom_shutdown_app(&handle->qseecom_handle); + if (rc) { + pr_err("qseecom_shutdown_app failed for HDCP2P2 (%d)\n", rc); + return rc; + } + + handle->hdcp_state &= ~HDCP_STATE_APP_LOADED; + pr_debug("%s app unloaded\n", handle->app_name); + + return rc; +error: + qseecom_shutdown_app(&handle->hdcpsrm_qseecom_handle); + return rc; +} + +static int hdcp2_verify_key(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(verify_key); + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", handle->app_name); + rc = -EINVAL; + goto error; + } + + rc = hdcp2_app_process_cmd(verify_key); + pr_debug("verify_key = %d\n", rc); + +error: + return rc; +} + +bool hdcp2_feature_supported(void *data) +{ + int rc = 0; + bool supported = false; + struct hdcp2_handle *handle = data; + + if (!handle) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto error; + } + + if (handle->feature_supported) { + supported = true; + goto error; + } + + rc = hdcp2_app_load(handle); + if (!rc) { + if (!hdcp2_verify_key(handle)) { + pr_debug("HDCP 2.2 supported\n"); + handle->feature_supported = true; + supported = true; + } + hdcp2_app_unload(handle); + } +error: + return supported; +} +EXPORT_SYMBOL(hdcp2_feature_supported); + +static int hdcp2_app_session_init(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(session_init); + + if (!handle->qseecom_handle || !handle->qseecom_handle->sbuf) { + pr_err("invalid handle\n"); + rc = -EINVAL; + goto error; + } + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", handle->app_name); + rc = -EINVAL; + goto error; + } + + if (handle->hdcp_state & HDCP_STATE_SESSION_INIT) { + pr_err("session already initialized\n"); + goto error; + } + + req_buf->deviceid = handle->device_type; + + rc = hdcp2_app_process_cmd(session_init); + if (rc) + goto error; + + pr_debug("session id %d\n", rsp_buf->sessionid); + + handle->session_id = rsp_buf->sessionid; + handle->hdcp_state |= HDCP_STATE_SESSION_INIT; + + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_session_deinit(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(session_deinit); + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", handle->app_name); + rc = -EINVAL; + goto error; + } + + if (!(handle->hdcp_state & HDCP_STATE_SESSION_INIT)) { + pr_err("session not initialized\n"); + rc = -EINVAL; + goto error; + } + + req_buf->sessionid = handle->session_id; + + rc = hdcp2_app_process_cmd(session_deinit); + if (rc) + goto error; + + handle->hdcp_state &= ~HDCP_STATE_SESSION_INIT; + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_tx_deinit(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(tx_deinit); + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", handle->app_name); + rc = -EINVAL; + goto error; + } + + if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { + pr_err("txmtr not initialized\n"); + rc = -EINVAL; + goto error; + } + + req_buf->ctxhandle = handle->tz_ctxhandle; + + rc = hdcp2_app_process_cmd(tx_deinit); + if (rc) + goto error; + + handle->hdcp_state &= ~HDCP_STATE_TXMTR_INIT; + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_start_auth(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(start_auth); + + if (!(handle->hdcp_state & HDCP_STATE_SESSION_INIT)) { + pr_err("session not initialized\n"); + rc = -EINVAL; + goto error; + } + + if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { + pr_err("txmtr not initialized\n"); + rc = -EINVAL; + goto error; + } + + req_buf->ctxHandle = handle->tz_ctxhandle; + + rc = hdcp2_app_process_cmd(start_auth); + if (rc) + goto error; + + handle->app_data.response.data = rsp_buf->message; + handle->app_data.response.length = rsp_buf->msglen; + handle->app_data.timeout = rsp_buf->timeout; + handle->app_data.repeater_flag = false; + + handle->tz_ctxhandle = rsp_buf->ctxhandle; + + pr_debug("success\n"); +error: + return rc; +} + +static int hdcp2_app_start(struct hdcp2_handle *handle) +{ + int rc = 0; + + rc = hdcp2_app_load(handle); + if (rc) + goto error; + + if (!handle->legacy_app) { + rc = hdcp2_app_session_init(handle); + if (rc) + goto error; + } + + if (handle->tx_init == NULL) { + pr_err("invalid txmtr init function pointer\n"); + rc = -EINVAL; + goto error; + } + + rc = handle->tx_init(handle); + +error: + return rc; +} + +static int hdcp2_app_stop(struct hdcp2_handle *handle) +{ + int rc = 0; + + rc = hdcp2_app_tx_deinit(handle); + if (rc) + goto end; + + if (!handle->legacy_app) { + rc = hdcp2_app_session_deinit(handle); + if (rc) + goto end; + } + + rc = hdcp2_app_unload(handle); +end: + return rc; +} + +static int hdcp2_app_process_msg(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(rcvd_msg); + + if (!handle->app_data.request.data) { + pr_err("invalid request buffer\n"); + rc = -EINVAL; + goto error; + } + + req_buf->msglen = handle->app_data.request.length; + req_buf->ctxhandle = handle->tz_ctxhandle; + + rc = hdcp2_app_process_cmd(rcvd_msg); + if (rc) + goto error; + + /* check if it's a repeater */ + if (rsp_buf->flag == HDCP_TXMTR_SUBSTATE_WAITING_FOR_RECIEVERID_LIST) + handle->app_data.repeater_flag = true; + + handle->app_data.response.data = rsp_buf->msg; + handle->app_data.response.length = rsp_buf->msglen; + handle->app_data.timeout = rsp_buf->timeout; +error: + return rc; +} + +static int hdcp2_app_timeout(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(send_timeout); + + rc = hdcp2_app_process_cmd(send_timeout); + if (rc) + goto error; + + handle->app_data.response.data = rsp_buf->message; + handle->app_data.response.length = rsp_buf->msglen; + handle->app_data.timeout = rsp_buf->timeout; +error: + return rc; +} + +static int hdcp2_app_enable_encryption(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(set_hw_key); + + /* + * wait at least 200ms before enabling encryption + * as per hdcp2p2 specifications. + */ + msleep(SLEEP_SET_HW_KEY_MS); + + req_buf->ctxhandle = handle->tz_ctxhandle; + + rc = hdcp2_app_process_cmd(set_hw_key); + if (rc) + goto error; + + handle->hdcp_state |= HDCP_STATE_AUTHENTICATED; + + pr_debug("success\n"); + return rc; +error: + return rc; +} + +static int hdcp2_force_encryption_utility(struct hdcp2_handle *handle, + uint32_t enable) +{ + int rc = 0; + + hdcp2_app_init_var(force_encryption); + if (handle->hdcp_state == HDCP_STATE_AUTHENTICATED) + msleep(SLEEP_FORCE_ENCRYPTION_MS); + + req_buf->ctxhandle = handle->tz_ctxhandle; + req_buf->enable = enable; + + rc = hdcp2_app_process_cmd(force_encryption); + if (rc || (rsp_buf->commandid != hdcp_cmd_force_encryption)) + goto error; + + return 0; +error: + return rc; +} + +int hdcp2_force_encryption(void *ctx, uint32_t enable) +{ + int rc = 0; + struct hdcp2_handle *handle = NULL; + + if (!ctx) { + pr_err("invalid input\n"); + return -EINVAL; + } + + handle = ctx; + rc = hdcp2_force_encryption_utility(handle, enable); + if (rc) + goto error; + + pr_debug("success\n"); + return 0; +error: + pr_err("failed, rc=%d\n", rc); + return rc; +} +EXPORT_SYMBOL(hdcp2_force_encryption); + +static int hdcp2_app_query_stream(struct hdcp2_handle *handle) +{ + int rc = 0; + + hdcp2_app_init_var(query_stream_type); + + req_buf->ctxhandle = handle->tz_ctxhandle; + + rc = hdcp2_app_process_cmd(query_stream_type); + if (rc) + goto error; + + handle->app_data.response.data = rsp_buf->msg; + handle->app_data.response.length = rsp_buf->msglen; + handle->app_data.timeout = rsp_buf->timeout; +error: + return rc; +} + +static unsigned char *hdcp2_get_recv_buf(struct hdcp2_handle *handle) +{ + struct hdcp_rcvd_msg_req *req_buf; + + req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf); + return req_buf->msg; +} + +int hdcp2_app_comm(void *ctx, enum hdcp2_app_cmd cmd, + struct hdcp2_app_data *app_data) +{ + struct hdcp2_handle *handle = NULL; + int rc = 0; + + if (!ctx || !app_data) { + pr_err("invalid input\n"); + return -EINVAL; + } + + handle = ctx; + handle->app_data.request.length = app_data->request.length; + + pr_debug("command %s\n", hdcp2_app_cmd_str(cmd)); + + switch (cmd) { + case HDCP2_CMD_START: + rc = hdcp2_app_start(handle); + break; + case HDCP2_CMD_START_AUTH: + rc = hdcp2_app_start_auth(handle); + break; + case HDCP2_CMD_PROCESS_MSG: + rc = hdcp2_app_process_msg(handle); + break; + case HDCP2_CMD_TIMEOUT: + rc = hdcp2_app_timeout(handle); + break; + case HDCP2_CMD_EN_ENCRYPTION: + rc = hdcp2_app_enable_encryption(handle); + break; + case HDCP2_CMD_QUERY_STREAM: + rc = hdcp2_app_query_stream(handle); + break; + case HDCP2_CMD_STOP: + rc = hdcp2_app_stop(handle); + default: + goto error; + } + + if (rc) + goto error; + + handle->app_data.request.data = hdcp2_get_recv_buf(handle); + + app_data->request.data = handle->app_data.request.data; + app_data->request.length = handle->app_data.request.length; + app_data->response.data = handle->app_data.response.data; + app_data->response.length = handle->app_data.response.length; + app_data->timeout = handle->app_data.timeout; + app_data->repeater_flag = handle->app_data.repeater_flag; +error: + return rc; +} +EXPORT_SYMBOL(hdcp2_app_comm); + +static int hdcp2_open_stream_helper(struct hdcp2_handle *handle, + uint8_t vc_payload_id, + uint8_t stream_number, + uint32_t *stream_id) +{ + int rc = 0; + + hdcp2_app_init_var(session_open_stream); + + if (!(handle->hdcp_state & HDCP_STATE_SESSION_INIT)) { + pr_err("session not initialized\n"); + rc = -EINVAL; + goto error; + } + + if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { + pr_err("txmtr not initialized\n"); + rc = -EINVAL; + goto error; + } + + req_buf->sessionid = handle->session_id; + req_buf->vcpayloadid = vc_payload_id; + req_buf->stream_number = stream_number; + req_buf->streamMediaType = 0; + + rc = hdcp2_app_process_cmd(session_open_stream); + if (rc) + goto error; + + *stream_id = rsp_buf->streamid; + + pr_debug("success\n"); + +error: + return rc; +} + +int hdcp2_open_stream(void *ctx, uint8_t vc_payload_id, uint8_t stream_number, + uint32_t *stream_id) +{ + struct hdcp2_handle *handle = NULL; + + if (!ctx) { + pr_err("invalid input\n"); + return -EINVAL; + } + + handle = ctx; + + return hdcp2_open_stream_helper(handle, vc_payload_id, stream_number, + stream_id); +} +EXPORT_SYMBOL(hdcp2_open_stream); + +static int hdcp2_close_stream_helper(struct hdcp2_handle *handle, + uint32_t stream_id) +{ + int rc = 0; + + hdcp2_app_init_var(session_close_stream); + + if (!(handle->hdcp_state & HDCP_STATE_SESSION_INIT)) { + pr_err("session not initialized\n"); + rc = -EINVAL; + goto error; + } + + if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { + pr_err("txmtr not initialized\n"); + rc = -EINVAL; + goto error; + } + + req_buf->sessionid = handle->session_id; + req_buf->streamid = stream_id; + + rc = hdcp2_app_process_cmd(session_close_stream); + + if (rc) + goto error; + + pr_debug("success\n"); +error: + return rc; +} + +int hdcp2_close_stream(void *ctx, uint32_t stream_id) +{ + struct hdcp2_handle *handle = NULL; + + if (!ctx) { + pr_err("invalid input\n"); + return -EINVAL; + } + + handle = ctx; + + return hdcp2_close_stream_helper(handle, stream_id); +} +EXPORT_SYMBOL(hdcp2_close_stream); + +void *hdcp2_init(u32 device_type) +{ + struct hdcp2_handle *handle = NULL; + + handle = kzalloc(sizeof(struct hdcp2_handle), GFP_KERNEL); + if (!handle) + goto error; + + handle->device_type = device_type; + handle->app_name = HDCP2P2_APP_NAME; +error: + return handle; +} +EXPORT_SYMBOL(hdcp2_init); + +void hdcp2_deinit(void *ctx) +{ + kzfree(ctx); +} +EXPORT_SYMBOL(hdcp2_deinit); + +void *hdcp1_init(void) +{ + struct hdcp1_handle *handle = + kzalloc(sizeof(struct hdcp1_handle), GFP_KERNEL); + + if (!handle) + goto error; + + handle->app_name = HDCP1_APP_NAME; +error: + return handle; +} +EXPORT_SYMBOL(hdcp1_init); + +void hdcp1_deinit(void *data) +{ + kfree(data); +} +EXPORT_SYMBOL(hdcp1_deinit); + +static int hdcp1_count_ones(u8 *array, u8 len) +{ + int i, j, count = 0; + + for (i = 0; i < len; i++) + for (j = 0; j < 8; j++) + count += (((array[i] >> j) & 0x1) ? 1 : 0); + return count; +} + +static int hdcp1_validate_aksv(u32 aksv_msb, u32 aksv_lsb) +{ + int const number_of_ones = 20; + u8 aksv[5]; + + pr_debug("AKSV=%02x%08x\n", aksv_msb, aksv_lsb); + + aksv[0] = aksv_lsb & 0xFF; + aksv[1] = (aksv_lsb >> 8) & 0xFF; + aksv[2] = (aksv_lsb >> 16) & 0xFF; + aksv[3] = (aksv_lsb >> 24) & 0xFF; + aksv[4] = aksv_msb & 0xFF; + + /* check there are 20 ones in AKSV */ + if (hdcp1_count_ones(aksv, 5) != number_of_ones) { + pr_err("AKSV bit count failed\n"); + return -EINVAL; + } + + return 0; +} + + +static int hdcp1_set_key(struct hdcp1_handle *hdcp1_handle, u32 *aksv_msb, + u32 *aksv_lsb) +{ + int rc = 0; + struct hdcp1_key_set_req *key_set_req; + struct hdcp1_key_set_rsp *key_set_rsp; + struct qseecom_handle *handle = NULL; + + if (aksv_msb == NULL || aksv_lsb == NULL) { + pr_err("invalid aksv\n"); + return -EINVAL; + } + + if (!hdcp1_handle || !hdcp1_handle->qseecom_handle) { + pr_err("invalid HDCP 1.x handle\n"); + return -EINVAL; + } + + if (!(hdcp1_handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", hdcp1_handle->app_name); + return -EINVAL; + } + + handle = hdcp1_handle->qseecom_handle; + + /* set keys and request aksv */ + key_set_req = (struct hdcp1_key_set_req *)handle->sbuf; + key_set_req->commandid = HDCP1_SET_KEY; + key_set_rsp = (struct hdcp1_key_set_rsp *)(handle->sbuf + + QSEECOM_ALIGN(sizeof(struct hdcp1_key_set_req))); + rc = qseecom_send_command(handle, key_set_req, + QSEECOM_ALIGN(sizeof + (struct hdcp1_key_set_req)), + key_set_rsp, + QSEECOM_ALIGN(sizeof + (struct hdcp1_key_set_rsp))); + + if (rc < 0) { + pr_err("qseecom cmd failed err=%d\n", rc); + return -ENOKEY; + } + + rc = key_set_rsp->ret; + if (rc) { + pr_err("set key cmd failed, rsp=%d\n", key_set_rsp->ret); + return -ENOKEY; + } + + /* copy bytes into msb and lsb */ + *aksv_msb = key_set_rsp->ksv[0] << 24 | key_set_rsp->ksv[1] << 16 | + key_set_rsp->ksv[2] << 8 | key_set_rsp->ksv[3]; + *aksv_lsb = key_set_rsp->ksv[4] << 24 | key_set_rsp->ksv[5] << 16 | + key_set_rsp->ksv[6] << 8 | key_set_rsp->ksv[7]; + + rc = hdcp1_validate_aksv(*aksv_msb, *aksv_lsb); + if (rc) { + pr_err("aksv validation failed (%d)\n", rc); + return rc; + } + + return 0; +} + +static int hdcp1_app_load(struct hdcp1_handle *handle) +{ + int rc = 0; + + if (!handle) { + pr_err("invalid handle\n"); + goto error; + } + + rc = qseecom_start_app(&handle->qseecom_handle, handle->app_name, + QSEECOM_SBUFF_SIZE); + if (rc) { + pr_err("%s app load failed (%d)\n", handle->app_name, rc); + goto error; + } + + handle->hdcp_state |= HDCP_STATE_APP_LOADED; + pr_debug("%s app loaded\n", handle->app_name); + +error: + return rc; +} + +static void hdcp1_app_unload(struct hdcp1_handle *handle) +{ + int rc = 0; + + if (!handle || !handle->qseecom_handle) { + pr_err("invalid handle\n"); + return; + } + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_warn("%s app not loaded\n", handle->app_name); + return; + } + + /* deallocate the resources for qseecom HDCP 1.x handle */ + rc = qseecom_shutdown_app(&handle->qseecom_handle); + if (rc) { + pr_err("%s app unload failed (%d)\n", handle->app_name, rc); + return; + } + + handle->hdcp_state &= ~HDCP_STATE_APP_LOADED; + pr_debug("%s app unloaded\n", handle->app_name); +} + +static int hdcp1_verify_key(struct hdcp1_handle *hdcp1_handle) +{ + int rc = 0; + struct hdcp1_key_verify_req *key_verify_req; + struct hdcp1_key_verify_rsp *key_verify_rsp; + struct qseecom_handle *handle = NULL; + + if (!hdcp1_handle || !hdcp1_handle->qseecom_handle) { + pr_err("invalid HDCP 1.x handle\n"); + return -EINVAL; + } + + if (!(hdcp1_handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", hdcp1_handle->app_name); + return -EINVAL; + } + + handle = hdcp1_handle->qseecom_handle; + + key_verify_req = (struct hdcp1_key_verify_req *)handle->sbuf; + key_verify_req->commandid = HDCP1_KEY_VERIFY; + key_verify_rsp = (struct hdcp1_key_verify_rsp *)(handle->sbuf + + QSEECOM_ALIGN(sizeof(struct hdcp1_key_verify_req))); + rc = qseecom_send_command(handle, key_verify_req, + QSEECOM_ALIGN(sizeof + (struct hdcp1_key_verify_req)), + key_verify_rsp, + QSEECOM_ALIGN(sizeof + (struct hdcp1_key_set_rsp))); + + if (rc < 0) { + pr_err("command HDCP1_KEY_VERIFY failed (%d)\n", rc); + return -EINVAL; + } + + rc = key_verify_rsp->ret; + if (rc) { + pr_err("key_verify failed, rsp=%d\n", key_verify_rsp->ret); + return -EINVAL; + } + + pr_debug("success\n"); + + return 0; +} + +bool hdcp1_feature_supported(void *data) +{ + bool supported = false; + struct hdcp1_handle *handle = data; + int rc = 0; + + if (!handle) { + pr_err("invalid handle\n"); + goto error; + } + + if (handle->feature_supported) { + supported = true; + goto error; + } + + rc = hdcp1_app_load(handle); + if (!rc && (handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + if (!hdcp1_verify_key(handle)) { + pr_debug("HDCP 1.x supported\n"); + handle->feature_supported = true; + supported = true; + } + hdcp1_app_unload(handle); + } +error: + return supported; +} +EXPORT_SYMBOL(hdcp1_feature_supported); + +int hdcp1_set_enc(void *data, bool enable) +{ + int rc = 0; + struct hdcp1_set_enc_req *set_enc_req; + struct hdcp1_set_enc_rsp *set_enc_rsp; + struct hdcp1_handle *hdcp1_handle = data; + struct qseecom_handle *handle = NULL; + + if (!hdcp1_handle || !hdcp1_handle->qseecom_handle) { + pr_err("invalid HDCP 1.x handle\n"); + return -EINVAL; + } + + if (!hdcp1_handle->feature_supported) { + pr_err("HDCP 1.x not supported\n"); + return -EINVAL; + } + + if (!(hdcp1_handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("%s app not loaded\n", hdcp1_handle->app_name); + return -EINVAL; + } + + handle = hdcp1_handle->qseecom_handle; + + /* set keys and request aksv */ + set_enc_req = (struct hdcp1_set_enc_req *)handle->sbuf; + set_enc_req->commandid = HDCP1_SET_ENC; + set_enc_req->enable = enable; + set_enc_rsp = (struct hdcp1_set_enc_rsp *)(handle->sbuf + + QSEECOM_ALIGN(sizeof(struct hdcp1_set_enc_req))); + rc = qseecom_send_command(handle, set_enc_req, + QSEECOM_ALIGN(sizeof + (struct hdcp1_set_enc_req)), + set_enc_rsp, + QSEECOM_ALIGN(sizeof + (struct hdcp1_set_enc_rsp))); + + if (rc < 0) { + pr_err("qseecom cmd failed err=%d\n", rc); + return -EINVAL; + } + + rc = set_enc_rsp->ret; + if (rc) { + pr_err("enc cmd failed, rsp=%d\n", set_enc_rsp->ret); + return -EINVAL; + } + + pr_debug("success\n"); + return 0; +} +EXPORT_SYMBOL(hdcp1_set_enc); + +int hdcp1_start(void *data, u32 *aksv_msb, u32 *aksv_lsb) +{ + int rc = 0; + struct hdcp1_handle *hdcp1_handle = data; + + if (!aksv_msb || !aksv_lsb) { + pr_err("invalid aksv output buffer\n"); + rc = -EINVAL; + goto error; + } + + if (!hdcp1_handle) { + pr_err("invalid handle\n"); + rc = -EINVAL; + goto error; + } + + if (!hdcp1_handle->feature_supported) { + pr_err("feature not supported\n"); + rc = -EINVAL; + goto error; + } + + if (hdcp1_handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_debug("%s app already loaded\n", hdcp1_handle->app_name); + goto error; + } + + rc = hdcp1_app_load(hdcp1_handle); + if (rc) + goto error; + + rc = hdcp1_set_key(hdcp1_handle, aksv_msb, aksv_lsb); + if (rc) + goto key_error; + + pr_debug("success\n"); + return rc; + +key_error: + hdcp1_app_unload(hdcp1_handle); +error: + return rc; +} +EXPORT_SYMBOL(hdcp1_start); + +void hdcp1_stop(void *data) +{ + struct hdcp1_handle *hdcp1_handle = data; + + if (!hdcp1_handle || !hdcp1_handle->qseecom_handle) { + pr_err("invalid handle\n"); + return; + } + + if (!(hdcp1_handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_debug("%s app not loaded\n", hdcp1_handle->app_name); + return; + } + + hdcp1_app_unload(hdcp1_handle); +} +EXPORT_SYMBOL(hdcp1_stop); + +static int __init hdcp_module_init(void){ return 0; } +static void __exit hdcp_module_exit(void){ return; } + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HDCP driver"); + +module_init(hdcp_module_init); +module_exit(hdcp_module_exit);