Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,33 @@
#
# Copyright 2019 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
#
# Makefile for the 'hdcp' sub-module of DAL.
#
HDCP = hdcp_ddc.o hdcp_log.o hdcp_psp.o hdcp.o \
hdcp1_execution.o hdcp1_transition.o \
hdcp2_execution.o hdcp2_transition.o
AMD_DAL_HDCP = $(addprefix $(AMDDALPATH)/modules/hdcp/,$(HDCP))
#$(info ************ DAL-HDCP_MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_HDCP)
@@ -0,0 +1,601 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include "hdcp.h"
static void push_error_status(struct mod_hdcp *hdcp,
enum mod_hdcp_status status)
{
struct mod_hdcp_trace *trace = &hdcp->connection.trace;
const uint8_t retry_limit = hdcp->connection.link.adjust.retry_limit;
if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) {
trace->errors[trace->error_count].status = status;
trace->errors[trace->error_count].state_id = hdcp->state.id;
trace->error_count++;
HDCP_ERROR_TRACE(hdcp, status);
}
if (is_hdcp1(hdcp)) {
hdcp->connection.hdcp1_retry_count++;
if (hdcp->connection.hdcp1_retry_count == retry_limit)
hdcp->connection.link.adjust.hdcp1.disable = 1;
} else if (is_hdcp2(hdcp)) {
hdcp->connection.hdcp2_retry_count++;
if (hdcp->connection.hdcp2_retry_count == retry_limit)
hdcp->connection.link.adjust.hdcp2.disable = 1;
}
}
static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp)
{
int i, is_auth_needed = 0;
/* if all displays on the link don't need authentication,
* hdcp is not desired
*/
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {
if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&
hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {
is_auth_needed = 1;
break;
}
}
return is_auth_needed &&
!hdcp->connection.link.adjust.hdcp1.disable &&
!is_frl_hdcp(hdcp) &&
!hdcp->connection.is_hdcp1_revoked;
}
static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp)
{
int i, is_auth_needed = 0;
/* if all displays on the link don't need authentication,
* hdcp is not desired
*/
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {
if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&
hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {
is_auth_needed = 1;
break;
}
}
return is_auth_needed &&
!hdcp->connection.link.adjust.hdcp2.disable &&
!hdcp->connection.is_hdcp2_revoked;
}
static enum mod_hdcp_status execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
union mod_hdcp_transition_input *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (is_in_initialized_state(hdcp)) {
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
/* initialize transition input */
memset(input, 0, sizeof(union mod_hdcp_transition_input));
} else if (is_in_cp_not_desired_state(hdcp)) {
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
} else if (is_in_hdcp1_states(hdcp)) {
status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1);
} else if (is_in_hdcp1_dp_states(hdcp)) {
status = mod_hdcp_hdcp1_dp_execution(hdcp,
event_ctx, &input->hdcp1);
} else if (is_in_hdcp2_states(hdcp)) {
status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2);
} else if (is_in_hdcp2_dp_states(hdcp)) {
status = mod_hdcp_hdcp2_dp_execution(hdcp,
event_ctx, &input->hdcp2);
} else {
event_ctx->unexpected_event = 1;
goto out;
}
out:
return status;
}
static enum mod_hdcp_status transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
union mod_hdcp_transition_input *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->unexpected_event)
goto out;
if (is_in_initialized_state(hdcp)) {
if (is_dp_hdcp(hdcp))
if (is_cp_desired_hdcp2(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE);
} else if (is_cp_desired_hdcp1(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);
set_auth_complete(hdcp, output);
}
else if (is_hdmi_dvi_sl_hdcp(hdcp))
if (is_cp_desired_hdcp2(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A0_KNOWN_HDCP2_CAPABLE_RX);
} else if (is_cp_desired_hdcp1(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);
set_auth_complete(hdcp, output);
}
else {
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);
set_auth_complete(hdcp, output);
}
} else if (is_in_cp_not_desired_state(hdcp)) {
increment_stay_counter(hdcp);
} else if (is_in_hdcp1_states(hdcp)) {
status = mod_hdcp_hdcp1_transition(hdcp,
event_ctx, &input->hdcp1, output);
} else if (is_in_hdcp1_dp_states(hdcp)) {
status = mod_hdcp_hdcp1_dp_transition(hdcp,
event_ctx, &input->hdcp1, output);
} else if (is_in_hdcp2_states(hdcp)) {
status = mod_hdcp_hdcp2_transition(hdcp,
event_ctx, &input->hdcp2, output);
} else if (is_in_hdcp2_dp_states(hdcp)) {
status = mod_hdcp_hdcp2_dp_transition(hdcp,
event_ctx, &input->hdcp2, output);
} else {
status = MOD_HDCP_STATUS_INVALID_STATE;
}
out:
return status;
}
static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (is_hdcp1(hdcp)) {
if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) {
/* TODO - update psp to unify create session failure
* recovery between hdcp1 and 2.
*/
mod_hdcp_hdcp1_destroy_session(hdcp);
}
HDCP_TOP_RESET_AUTH_TRACE(hdcp);
memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));
memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else if (is_hdcp2(hdcp)) {
if (hdcp->auth.trans_input.hdcp2.create_session == PASS) {
status = mod_hdcp_hdcp2_destroy_session(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS) {
output->callback_needed = 0;
output->watchdog_timer_needed = 0;
goto out;
}
}
HDCP_TOP_RESET_AUTH_TRACE(hdcp);
memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));
memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else if (is_in_cp_not_desired_state(hdcp)) {
HDCP_TOP_RESET_AUTH_TRACE(hdcp);
memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));
memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));
set_state_id(hdcp, output, HDCP_INITIALIZED);
}
out:
/* stop callback and watchdog requests from previous authentication*/
output->watchdog_timer_stop = 1;
output->callback_stop = 1;
return status;
}
static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
memset(output, 0, sizeof(struct mod_hdcp_output));
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
if (current_state(hdcp) != HDCP_UNINITIALIZED) {
HDCP_TOP_RESET_CONN_TRACE(hdcp);
set_state_id(hdcp, output, HDCP_UNINITIALIZED);
}
memset(&hdcp->connection, 0, sizeof(hdcp->connection));
out:
return status;
}
static enum mod_hdcp_status update_display_adjustments(struct mod_hdcp *hdcp,
struct mod_hdcp_display *display,
struct mod_hdcp_display_adjustment *adj)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;
if (is_in_authenticated_states(hdcp) &&
is_dp_mst_hdcp(hdcp) &&
display->adjust.disable == true &&
adj->disable == false) {
display->adjust.disable = false;
if (is_hdcp1(hdcp))
status = mod_hdcp_hdcp1_enable_dp_stream_encryption(hdcp);
else if (is_hdcp2(hdcp))
status = mod_hdcp_hdcp2_enable_dp_stream_encryption(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
display->adjust.disable = true;
}
if (status == MOD_HDCP_STATUS_SUCCESS &&
memcmp(adj, &display->adjust,
sizeof(struct mod_hdcp_display_adjustment)) != 0)
status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;
return status;
}
/*
* Implementation of functions in mod_hdcp.h
*/
size_t mod_hdcp_get_memory_size(void)
{
return sizeof(struct mod_hdcp);
}
enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp,
struct mod_hdcp_config *config)
{
struct mod_hdcp_output output;
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
memset(&output, 0, sizeof(output));
hdcp->config = *config;
HDCP_TOP_INTERFACE_TRACE(hdcp);
status = reset_connection(hdcp, &output);
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_output output;
HDCP_TOP_INTERFACE_TRACE(hdcp);
memset(&output, 0, sizeof(output));
status = reset_connection(hdcp, &output);
if (status == MOD_HDCP_STATUS_SUCCESS)
memset(hdcp, 0, sizeof(struct mod_hdcp));
else
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,
struct mod_hdcp_link *link, struct mod_hdcp_display *display,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display_container = NULL;
HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index);
memset(output, 0, sizeof(struct mod_hdcp_output));
/* skip inactive display */
if (display->state != MOD_HDCP_DISPLAY_ACTIVE) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
/* check existing display container */
if (get_active_display_at_index(hdcp, display->index)) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
/* find an empty display container */
display_container = get_empty_display_container(hdcp);
if (!display_container) {
status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND;
goto out;
}
/* reset existing authentication status */
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* reset retry counters */
reset_retry_counts(hdcp);
/* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* add display to connection */
hdcp->connection.link = *link;
*display_container = *display;
status = mod_hdcp_add_display_to_topology(hdcp, display_container);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* request authentication */
if (current_state(hdcp) != HDCP_INITIALIZED)
set_state_id(hdcp, output, HDCP_INITIALIZED);
callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output);
out:
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,
uint8_t index, struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display = NULL;
HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);
memset(output, 0, sizeof(struct mod_hdcp_output));
/* find display in connection */
display = get_active_display_at_index(hdcp, index);
if (!display) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
/* stop current authentication */
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* clear retry counters */
reset_retry_counts(hdcp);
/* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* remove display */
status = mod_hdcp_remove_display_from_topology(hdcp, index);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
memset(display, 0, sizeof(struct mod_hdcp_display));
/* request authentication when connection is not reset */
if (current_state(hdcp) != HDCP_UNINITIALIZED)
callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000,
output);
out:
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,
uint8_t index,
struct mod_hdcp_link_adjustment *link_adjust,
struct mod_hdcp_display_adjustment *display_adjust,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display = NULL;
HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);
memset(output, 0, sizeof(struct mod_hdcp_output));
/* find display in connection */
display = get_active_display_at_index(hdcp, index);
if (!display) {
status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;
goto out;
}
/* skip if no changes */
if (memcmp(link_adjust, &hdcp->connection.link.adjust,
sizeof(struct mod_hdcp_link_adjustment)) == 0 &&
memcmp(display_adjust, &display->adjust,
sizeof(struct mod_hdcp_display_adjustment)) == 0) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
if (memcmp(link_adjust, &hdcp->connection.link.adjust,
sizeof(struct mod_hdcp_link_adjustment)) == 0 &&
memcmp(display_adjust, &display->adjust,
sizeof(struct mod_hdcp_display_adjustment)) != 0) {
status = update_display_adjustments(hdcp, display, display_adjust);
if (status != MOD_HDCP_STATUS_NOT_IMPLEMENTED)
goto out;
}
/* stop current authentication */
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* clear retry counters */
reset_retry_counts(hdcp);
/* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* set new adjustment */
hdcp->connection.link.adjust = *link_adjust;
display->adjust = *display_adjust;
/* request authentication when connection is not reset */
if (current_state(hdcp) != HDCP_UNINITIALIZED)
/* wait 100ms to debounce simultaneous updates for different indices */
callback_in_ms(100, output);
out:
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp,
uint8_t index, struct mod_hdcp_display_query *query)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display = NULL;
/* find display in connection */
display = get_active_display_at_index(hdcp, index);
if (!display) {
status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;
goto out;
}
/* populate query */
query->link = &hdcp->connection.link;
query->display = display;
query->trace = &hdcp->connection.trace;
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
if (is_display_encryption_enabled(display)) {
if (is_hdcp1(hdcp)) {
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON;
} else if (is_hdcp2(hdcp)) {
if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0)
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;
else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1)
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;
else
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON;
}
} else {
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
}
out:
return status;
}
enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
HDCP_TOP_INTERFACE_TRACE(hdcp);
status = reset_connection(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp,
enum mod_hdcp_event event, struct mod_hdcp_output *output)
{
enum mod_hdcp_status exec_status, trans_status, reset_status, status;
struct mod_hdcp_event_context event_ctx;
HDCP_EVENT_TRACE(hdcp, event);
memset(output, 0, sizeof(struct mod_hdcp_output));
memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context));
event_ctx.event = event;
/* execute and transition */
exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input);
trans_status = transition(
hdcp, &event_ctx, &hdcp->auth.trans_input, output);
if (trans_status == MOD_HDCP_STATUS_SUCCESS) {
status = MOD_HDCP_STATUS_SUCCESS;
} else if (exec_status == MOD_HDCP_STATUS_SUCCESS) {
status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE;
push_error_status(hdcp, status);
} else {
status = exec_status;
push_error_status(hdcp, status);
}
/* reset authentication if needed */
if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) {
mod_hdcp_log_ddc_trace(hdcp);
reset_status = reset_authentication(hdcp, output);
if (reset_status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, reset_status);
}
/* Clear CP_IRQ status if needed */
if (event_ctx.event == MOD_HDCP_EVENT_CPIRQ) {
status = mod_hdcp_clear_cp_irq_status(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
}
return status;
}
enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(
enum signal_type signal)
{
enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF;
switch (signal) {
case SIGNAL_TYPE_DVI_SINGLE_LINK:
case SIGNAL_TYPE_HDMI_TYPE_A:
case SIGNAL_TYPE_HDMI_FRL:
mode = MOD_HDCP_MODE_DEFAULT;
break;
case SIGNAL_TYPE_EDP:
case SIGNAL_TYPE_DISPLAY_PORT:
case SIGNAL_TYPE_DISPLAY_PORT_MST:
mode = MOD_HDCP_MODE_DP;
break;
default:
break;
}
return mode;
}
@@ -0,0 +1,593 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef HDCP_H_
#define HDCP_H_
#include "mod_hdcp.h"
#include "hdcp_log.h"
#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_hdcp_helper.h>
enum mod_hdcp_trans_input_result {
UNKNOWN = 0,
PASS,
FAIL
};
struct mod_hdcp_transition_input_hdcp1 {
uint8_t bksv_read;
uint8_t bksv_validation;
uint8_t create_session;
uint8_t an_write;
uint8_t aksv_write;
uint8_t ainfo_write;
uint8_t bcaps_read;
uint8_t r0p_read;
uint8_t rx_validation;
uint8_t encryption;
uint8_t link_maintenance;
uint8_t ready_check;
uint8_t bstatus_read;
uint8_t max_cascade_check;
uint8_t max_devs_check;
uint8_t device_count_check;
uint8_t ksvlist_read;
uint8_t vp_read;
uint8_t ksvlist_vp_validation;
uint8_t hdcp_capable_dp;
uint8_t binfo_read_dp;
uint8_t r0p_available_dp;
uint8_t link_integrity_check;
uint8_t reauth_request_check;
uint8_t stream_encryption_dp;
};
struct mod_hdcp_transition_input_hdcp2 {
uint8_t hdcp2version_read;
uint8_t hdcp2_capable_check;
uint8_t create_session;
uint8_t ake_init_prepare;
uint8_t ake_init_write;
uint8_t rxstatus_read;
uint8_t ake_cert_available;
uint8_t ake_cert_read;
uint8_t ake_cert_validation;
uint8_t stored_km_write;
uint8_t no_stored_km_write;
uint8_t h_prime_available;
uint8_t h_prime_read;
uint8_t pairing_available;
uint8_t pairing_info_read;
uint8_t h_prime_validation;
uint8_t lc_init_prepare;
uint8_t lc_init_write;
uint8_t l_prime_available_poll;
uint8_t l_prime_read;
uint8_t l_prime_combo_read;
uint8_t l_prime_validation;
uint8_t eks_prepare;
uint8_t eks_write;
uint8_t enable_encryption;
uint8_t reauth_request_check;
uint8_t rx_id_list_read;
uint8_t device_count_check;
uint8_t rx_id_list_validation;
uint8_t repeater_auth_ack_write;
uint8_t prepare_stream_manage;
uint8_t stream_manage_write;
uint8_t stream_ready_available;
uint8_t stream_ready_read;
uint8_t stream_ready_validation;
uint8_t rx_caps_read_dp;
uint8_t content_stream_type_write;
uint8_t link_integrity_check_dp;
uint8_t stream_encryption_dp;
};
union mod_hdcp_transition_input {
struct mod_hdcp_transition_input_hdcp1 hdcp1;
struct mod_hdcp_transition_input_hdcp2 hdcp2;
};
struct mod_hdcp_message_hdcp1 {
uint8_t an[8];
uint8_t aksv[5];
uint8_t ainfo;
uint8_t bksv[5];
uint16_t r0p;
uint8_t bcaps;
uint16_t bstatus;
uint8_t ksvlist[635];
uint16_t ksvlist_size;
uint8_t vp[20];
uint16_t binfo_dp;
};
struct mod_hdcp_message_hdcp2 {
uint8_t hdcp2version_hdmi;
uint8_t rxcaps_dp[3];
uint8_t rxstatus[2];
uint8_t ake_init[12];
uint8_t ake_cert[534];
uint8_t ake_no_stored_km[129];
uint8_t ake_stored_km[33];
uint8_t ake_h_prime[33];
uint8_t ake_pairing_info[17];
uint8_t lc_init[9];
uint8_t lc_l_prime[33];
uint8_t ske_eks[25];
uint8_t rx_id_list[177]; // 22 + 5 * 31
uint16_t rx_id_list_size;
uint8_t repeater_auth_ack[17];
uint8_t repeater_auth_stream_manage[68]; // 6 + 2 * 31
uint16_t stream_manage_size;
uint8_t repeater_auth_stream_ready[33];
uint8_t rxstatus_dp;
uint8_t content_stream_type_dp[2];
};
union mod_hdcp_message {
struct mod_hdcp_message_hdcp1 hdcp1;
struct mod_hdcp_message_hdcp2 hdcp2;
};
struct mod_hdcp_auth_counters {
uint8_t stream_management_retry_count;
};
/* contains values per connection */
struct mod_hdcp_connection {
struct mod_hdcp_link link;
uint8_t is_repeater;
uint8_t is_km_stored;
uint8_t is_hdcp1_revoked;
uint8_t is_hdcp2_revoked;
struct mod_hdcp_trace trace;
uint8_t hdcp1_retry_count;
uint8_t hdcp2_retry_count;
};
/* contains values per authentication cycle */
struct mod_hdcp_authentication {
uint32_t id;
union mod_hdcp_message msg;
union mod_hdcp_transition_input trans_input;
struct mod_hdcp_auth_counters count;
};
/* contains values per state change */
struct mod_hdcp_state {
uint8_t id;
uint32_t stay_count;
};
/* per event in a state */
struct mod_hdcp_event_context {
enum mod_hdcp_event event;
uint8_t rx_id_list_ready;
uint8_t unexpected_event;
};
struct mod_hdcp {
/* per link */
struct mod_hdcp_config config;
/* per connection */
struct mod_hdcp_connection connection;
/* per displays */
struct mod_hdcp_display displays[MAX_NUM_OF_DISPLAYS];
/* per authentication attempt */
struct mod_hdcp_authentication auth;
/* per state in an authentication */
struct mod_hdcp_state state;
/* reserved memory buffer */
uint8_t buf[2025];
};
enum mod_hdcp_initial_state_id {
HDCP_UNINITIALIZED = 0x0,
HDCP_INITIAL_STATE_START = HDCP_UNINITIALIZED,
HDCP_INITIALIZED,
HDCP_CP_NOT_DESIRED,
HDCP_INITIAL_STATE_END = HDCP_CP_NOT_DESIRED
};
enum mod_hdcp_hdcp1_state_id {
HDCP1_STATE_START = HDCP_INITIAL_STATE_END,
H1_A0_WAIT_FOR_ACTIVE_RX,
H1_A1_EXCHANGE_KSVS,
H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER,
H1_A45_AUTHENTICATED,
H1_A8_WAIT_FOR_READY,
H1_A9_READ_KSV_LIST,
HDCP1_STATE_END = H1_A9_READ_KSV_LIST
};
enum mod_hdcp_hdcp1_dp_state_id {
HDCP1_DP_STATE_START = HDCP1_STATE_END,
D1_A0_DETERMINE_RX_HDCP_CAPABLE,
D1_A1_EXCHANGE_KSVS,
D1_A23_WAIT_FOR_R0_PRIME,
D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER,
D1_A4_AUTHENTICATED,
D1_A6_WAIT_FOR_READY,
D1_A7_READ_KSV_LIST,
HDCP1_DP_STATE_END = D1_A7_READ_KSV_LIST,
};
enum mod_hdcp_hdcp2_state_id {
HDCP2_STATE_START = HDCP1_DP_STATE_END,
H2_A0_KNOWN_HDCP2_CAPABLE_RX,
H2_A1_SEND_AKE_INIT,
H2_A1_VALIDATE_AKE_CERT,
H2_A1_SEND_NO_STORED_KM,
H2_A1_READ_H_PRIME,
H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME,
H2_A1_SEND_STORED_KM,
H2_A1_VALIDATE_H_PRIME,
H2_A2_LOCALITY_CHECK,
H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER,
H2_ENABLE_ENCRYPTION,
H2_A5_AUTHENTICATED,
H2_A6_WAIT_FOR_RX_ID_LIST,
H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK,
H2_A9_SEND_STREAM_MANAGEMENT,
H2_A9_VALIDATE_STREAM_READY,
HDCP2_STATE_END = H2_A9_VALIDATE_STREAM_READY,
};
enum mod_hdcp_hdcp2_dp_state_id {
HDCP2_DP_STATE_START = HDCP2_STATE_END,
D2_A0_DETERMINE_RX_HDCP_CAPABLE,
D2_A1_SEND_AKE_INIT,
D2_A1_VALIDATE_AKE_CERT,
D2_A1_SEND_NO_STORED_KM,
D2_A1_READ_H_PRIME,
D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME,
D2_A1_SEND_STORED_KM,
D2_A1_VALIDATE_H_PRIME,
D2_A2_LOCALITY_CHECK,
D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER,
D2_SEND_CONTENT_STREAM_TYPE,
D2_ENABLE_ENCRYPTION,
D2_A5_AUTHENTICATED,
D2_A6_WAIT_FOR_RX_ID_LIST,
D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK,
D2_A9_SEND_STREAM_MANAGEMENT,
D2_A9_VALIDATE_STREAM_READY,
HDCP2_DP_STATE_END = D2_A9_VALIDATE_STREAM_READY,
HDCP_STATE_END = HDCP2_DP_STATE_END,
};
/* hdcp1 executions and transitions */
typedef enum mod_hdcp_status (*mod_hdcp_action)(struct mod_hdcp *hdcp);
uint8_t mod_hdcp_execute_and_set(
mod_hdcp_action func, uint8_t *flag,
enum mod_hdcp_status *status, struct mod_hdcp *hdcp, char *str);
enum mod_hdcp_status mod_hdcp_hdcp1_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input);
enum mod_hdcp_status mod_hdcp_hdcp1_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input);
enum mod_hdcp_status mod_hdcp_hdcp1_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input,
struct mod_hdcp_output *output);
enum mod_hdcp_status mod_hdcp_hdcp1_dp_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input,
struct mod_hdcp_output *output);
/* hdcp2 executions and transitions */
enum mod_hdcp_status mod_hdcp_hdcp2_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input);
enum mod_hdcp_status mod_hdcp_hdcp2_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input);
enum mod_hdcp_status mod_hdcp_hdcp2_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output);
enum mod_hdcp_status mod_hdcp_hdcp2_dp_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output);
/* log functions */
void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size,
uint8_t *buf, uint32_t buf_size);
void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp);
/* TODO: add adjustment log */
/* psp functions */
enum mod_hdcp_status mod_hdcp_add_display_to_topology(
struct mod_hdcp *hdcp, struct mod_hdcp_display *display);
enum mod_hdcp_status mod_hdcp_remove_display_from_topology(
struct mod_hdcp *hdcp, uint8_t index);
enum mod_hdcp_status mod_hdcp_hdcp1_create_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_destroy_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_validate_rx(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_enable_encryption(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_validate_ksvlist_vp(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_enable_dp_stream_encryption(
struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_link_maintenance(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_create_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_destroy_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_ake_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_ake_cert(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_h_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_lc_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_l_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_eks(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_enable_encryption(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_rx_id_list(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_enable_dp_stream_encryption(
struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_stream_management(
struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_stream_ready(
struct mod_hdcp *hdcp);
/* ddc functions */
enum mod_hdcp_status mod_hdcp_read_bksv(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_bcaps(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_bstatus(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_r0p(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_ksvlist(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_vp(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_binfo(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_aksv(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_ainfo(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_an(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_hdcp2version(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_rxcaps(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_rxstatus(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_ake_cert(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_h_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_pairing_info(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_l_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_stream_ready(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_ake_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_no_stored_km(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_stored_km(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_lc_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_eks(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_repeater_auth_ack(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_stream_manage(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_content_type(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_clear_cp_irq_status(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_poll_read_lc_fw(struct mod_hdcp *hdcp);
/* hdcp version helpers */
static inline uint8_t is_dp_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP);
}
static inline uint8_t is_dp_mst_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP &&
hdcp->connection.link.dp.mst_enabled);
}
static inline uint8_t is_hdmi_dvi_sl_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT);
}
static inline uint8_t is_frl_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT &&
hdcp->connection.link.hdmi.frl_enabled);
}
/* hdcp state helpers */
static inline uint8_t current_state(struct mod_hdcp *hdcp)
{
return hdcp->state.id;
}
static inline void set_state_id(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output, uint8_t id)
{
memset(&hdcp->state, 0, sizeof(hdcp->state));
hdcp->state.id = id;
/* callback timer should be reset per state */
output->callback_stop = 1;
output->watchdog_timer_stop = 1;
HDCP_NEXT_STATE_TRACE(hdcp, id, output);
}
static inline uint8_t is_in_hdcp1_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP1_STATE_START &&
current_state(hdcp) <= HDCP1_STATE_END);
}
static inline uint8_t is_in_hdcp1_dp_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP1_DP_STATE_START &&
current_state(hdcp) <= HDCP1_DP_STATE_END);
}
static inline uint8_t is_in_hdcp2_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP2_STATE_START &&
current_state(hdcp) <= HDCP2_STATE_END);
}
static inline uint8_t is_in_hdcp2_dp_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP2_DP_STATE_START &&
current_state(hdcp) <= HDCP2_DP_STATE_END);
}
static inline uint8_t is_in_authenticated_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) == D1_A4_AUTHENTICATED ||
current_state(hdcp) == H1_A45_AUTHENTICATED ||
current_state(hdcp) == D2_A5_AUTHENTICATED ||
current_state(hdcp) == H2_A5_AUTHENTICATED);
}
static inline uint8_t is_hdcp1(struct mod_hdcp *hdcp)
{
return (is_in_hdcp1_states(hdcp) || is_in_hdcp1_dp_states(hdcp));
}
static inline uint8_t is_hdcp2(struct mod_hdcp *hdcp)
{
return (is_in_hdcp2_states(hdcp) || is_in_hdcp2_dp_states(hdcp));
}
static inline uint8_t is_in_cp_not_desired_state(struct mod_hdcp *hdcp)
{
return current_state(hdcp) == HDCP_CP_NOT_DESIRED;
}
static inline uint8_t is_in_initialized_state(struct mod_hdcp *hdcp)
{
return current_state(hdcp) == HDCP_INITIALIZED;
}
/* transition operation helpers */
static inline void increment_stay_counter(struct mod_hdcp *hdcp)
{
hdcp->state.stay_count++;
}
static inline void fail_and_restart_in_ms(uint16_t time,
enum mod_hdcp_status *status,
struct mod_hdcp_output *output)
{
output->callback_needed = 1;
output->callback_delay = time;
output->watchdog_timer_needed = 0;
output->watchdog_timer_delay = 0;
*status = MOD_HDCP_STATUS_RESET_NEEDED;
}
static inline void callback_in_ms(uint16_t time, struct mod_hdcp_output *output)
{
output->callback_needed = 1;
output->callback_delay = time;
}
static inline void set_watchdog_in_ms(struct mod_hdcp *hdcp, uint16_t time,
struct mod_hdcp_output *output)
{
(void)hdcp;
output->watchdog_timer_needed = 1;
output->watchdog_timer_delay = time;
}
static inline void set_auth_complete(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
output->auth_complete = 1;
HDCP_AUTH_COMPLETE_TRACE(hdcp);
}
/* connection topology helpers */
static inline uint8_t is_display_active(struct mod_hdcp_display *display)
{
return display->state >= MOD_HDCP_DISPLAY_ACTIVE;
}
static inline uint8_t is_display_encryption_enabled(struct mod_hdcp_display *display)
{
return display->state >= MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED;
}
static inline uint8_t get_active_display_count(struct mod_hdcp *hdcp)
{
uint8_t active_count = 0;
uint8_t i;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (is_display_active(&hdcp->displays[i]))
active_count++;
return active_count;
}
static inline struct mod_hdcp_display *get_first_active_display(
struct mod_hdcp *hdcp)
{
uint8_t i;
struct mod_hdcp_display *display = NULL;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (is_display_active(&hdcp->displays[i])) {
display = &hdcp->displays[i];
break;
}
return display;
}
static inline struct mod_hdcp_display *get_active_display_at_index(
struct mod_hdcp *hdcp, uint8_t index)
{
uint8_t i;
struct mod_hdcp_display *display = NULL;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (hdcp->displays[i].index == index &&
is_display_active(&hdcp->displays[i])) {
display = &hdcp->displays[i];
break;
}
return display;
}
static inline struct mod_hdcp_display *get_empty_display_container(
struct mod_hdcp *hdcp)
{
uint8_t i;
struct mod_hdcp_display *display = NULL;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (!is_display_active(&hdcp->displays[i])) {
display = &hdcp->displays[i];
break;
}
return display;
}
static inline void reset_retry_counts(struct mod_hdcp *hdcp)
{
hdcp->connection.hdcp1_retry_count = 0;
hdcp->connection.hdcp2_retry_count = 0;
}
#endif /* HDCP_H_ */
@@ -0,0 +1,544 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include "hdcp.h"
static inline enum mod_hdcp_status validate_bksv(struct mod_hdcp *hdcp)
{
uint64_t n = 0;
uint8_t count = 0;
enum mod_hdcp_status status;
u8 bksv[sizeof(n)] = { };
memcpy(bksv, hdcp->auth.msg.hdcp1.bksv, sizeof(hdcp->auth.msg.hdcp1.bksv));
n = *(uint64_t *)bksv;
while (n) {
count++;
n &= (n - 1);
}
if (count == 20) {
hdcp->connection.trace.hdcp1.attempt_count++;
status = MOD_HDCP_STATUS_SUCCESS;
} else {
status = MOD_HDCP_STATUS_HDCP1_INVALID_BKSV;
}
return status;
}
static inline enum mod_hdcp_status check_ksv_ready(struct mod_hdcp *hdcp)
{
if (is_dp_hdcp(hdcp))
return (hdcp->auth.msg.hdcp1.bstatus & DP_BSTATUS_READY) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY;
return (hdcp->auth.msg.hdcp1.bcaps & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY;
}
static inline enum mod_hdcp_status check_hdcp_capable_dp(struct mod_hdcp *hdcp)
{
return (hdcp->auth.msg.hdcp1.bcaps & DP_BCAPS_HDCP_CAPABLE) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE;
}
static inline enum mod_hdcp_status check_r0p_available_dp(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
status = (hdcp->auth.msg.hdcp1.bstatus &
DP_BSTATUS_R0_PRIME_READY) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING;
} else {
status = MOD_HDCP_STATUS_INVALID_OPERATION;
}
return status;
}
static inline enum mod_hdcp_status check_link_integrity_dp(
struct mod_hdcp *hdcp)
{
return (hdcp->auth.msg.hdcp1.bstatus &
DP_BSTATUS_LINK_FAILURE) ?
MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static inline enum mod_hdcp_status check_no_reauthentication_request_dp(
struct mod_hdcp *hdcp)
{
return (hdcp->auth.msg.hdcp1.bstatus & DP_BSTATUS_REAUTH_REQ) ?
MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED :
MOD_HDCP_STATUS_SUCCESS;
}
static inline enum mod_hdcp_status check_no_max_cascade(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = DRM_HDCP_MAX_CASCADE_EXCEEDED(hdcp->auth.msg.hdcp1.binfo_dp >> 8)
? MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE
: MOD_HDCP_STATUS_SUCCESS;
else
status = DRM_HDCP_MAX_CASCADE_EXCEEDED(hdcp->auth.msg.hdcp1.bstatus >> 8)
? MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE
: MOD_HDCP_STATUS_SUCCESS;
return status;
}
static inline enum mod_hdcp_status check_no_max_devs(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = DRM_HDCP_MAX_DEVICE_EXCEEDED(hdcp->auth.msg.hdcp1.binfo_dp) ?
MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
else
status = DRM_HDCP_MAX_DEVICE_EXCEEDED(hdcp->auth.msg.hdcp1.bstatus) ?
MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
return status;
}
static inline uint8_t get_device_count(struct mod_hdcp *hdcp)
{
return is_dp_hdcp(hdcp) ?
DRM_HDCP_NUM_DOWNSTREAM(hdcp->auth.msg.hdcp1.binfo_dp) :
DRM_HDCP_NUM_DOWNSTREAM(hdcp->auth.msg.hdcp1.bstatus);
}
static inline enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp)
{
/* Avoid device count == 0 to do authentication */
if (get_device_count(hdcp) == 0)
return MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE;
hdcp->connection.trace.hdcp1.downstream_device_count = get_device_count(hdcp);
/* Some MST display may choose to report the internal panel as an HDCP RX.
* To update this condition with 1(because the immediate repeater's internal
* panel is possibly not included in DEVICE_COUNT) + get_device_count(hdcp).
* Device count must be greater than or equal to tracked hdcp displays.
*/
return ((1 + get_device_count(hdcp)) < get_active_display_count(hdcp)) ?
MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static enum mod_hdcp_status wait_for_active_rx(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bksv,
&input->bksv_read, &status,
hdcp, "bksv_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps,
&input->bcaps_read, &status,
hdcp, "bcaps_read"))
goto out;
out:
return status;
}
static enum mod_hdcp_status exchange_ksvs(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_create_session,
&input->create_session, &status,
hdcp, "create_session"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_an,
&input->an_write, &status,
hdcp, "an_write"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_aksv,
&input->aksv_write, &status,
hdcp, "aksv_write"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bksv,
&input->bksv_read, &status,
hdcp, "bksv_read"))
goto out;
if (!mod_hdcp_execute_and_set(validate_bksv,
&input->bksv_validation, &status,
hdcp, "bksv_validation"))
goto out;
if (hdcp->auth.msg.hdcp1.ainfo) {
if (!mod_hdcp_execute_and_set(mod_hdcp_write_ainfo,
&input->ainfo_write, &status,
hdcp, "ainfo_write"))
goto out;
}
out:
return status;
}
static enum mod_hdcp_status computations_validate_rx_test_for_repeater(
struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_r0p,
&input->r0p_read, &status,
hdcp, "r0p_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_validate_rx,
&input->rx_validation, &status,
hdcp, "rx_validation"))
goto out;
if (hdcp->connection.is_repeater) {
if (!hdcp->connection.link.adjust.hdcp1.postpone_encryption)
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp1_enable_encryption,
&input->encryption, &status,
hdcp, "encryption"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_enable_encryption,
&input->encryption, &status,
hdcp, "encryption"))
goto out;
if (is_dp_mst_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp1_enable_dp_stream_encryption,
&input->stream_encryption_dp, &status,
hdcp, "stream_encryption_dp"))
goto out;
}
out:
return status;
}
static enum mod_hdcp_status authenticated(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
mod_hdcp_execute_and_set(mod_hdcp_hdcp1_link_maintenance,
&input->link_maintenance, &status,
hdcp, "link_maintenance");
out:
return status;
}
static enum mod_hdcp_status wait_for_ready(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (is_dp_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_link_integrity_dp,
&input->link_integrity_check, &status,
hdcp, "link_integrity_check"))
goto out;
if (!mod_hdcp_execute_and_set(check_no_reauthentication_request_dp,
&input->reauth_request_check, &status,
hdcp, "reauth_request_check"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps,
&input->bcaps_read, &status,
hdcp, "bcaps_read"))
goto out;
}
if (!mod_hdcp_execute_and_set(check_ksv_ready,
&input->ready_check, &status,
hdcp, "ready_check"))
goto out;
out:
return status;
}
static enum mod_hdcp_status read_ksv_list(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
uint8_t device_count;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (is_dp_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_binfo,
&input->binfo_read_dp, &status,
hdcp, "binfo_read_dp"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read"))
goto out;
}
if (!mod_hdcp_execute_and_set(check_no_max_cascade,
&input->max_cascade_check, &status,
hdcp, "max_cascade_check"))
goto out;
if (!mod_hdcp_execute_and_set(check_no_max_devs,
&input->max_devs_check, &status,
hdcp, "max_devs_check"))
goto out;
if (!mod_hdcp_execute_and_set(check_device_count,
&input->device_count_check, &status,
hdcp, "device_count_check"))
goto out;
device_count = get_device_count(hdcp);
hdcp->auth.msg.hdcp1.ksvlist_size = device_count*5;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_ksvlist,
&input->ksvlist_read, &status,
hdcp, "ksvlist_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_vp,
&input->vp_read, &status,
hdcp, "vp_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_validate_ksvlist_vp,
&input->ksvlist_vp_validation, &status,
hdcp, "ksvlist_vp_validation"))
goto out;
if (input->encryption != PASS)
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_enable_encryption,
&input->encryption, &status,
hdcp, "encryption"))
goto out;
if (is_dp_mst_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp1_enable_dp_stream_encryption,
&input->stream_encryption_dp, &status,
hdcp, "stream_encryption_dp"))
goto out;
out:
return status;
}
static enum mod_hdcp_status determine_rx_hdcp_capable_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps,
&input->bcaps_read, &status,
hdcp, "bcaps_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_hdcp_capable_dp,
&input->hdcp_capable_dp, &status,
hdcp, "hdcp_capable_dp"))
goto out;
out:
return status;
}
static enum mod_hdcp_status wait_for_r0_prime_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_r0p_available_dp,
&input->r0p_available_dp, &status,
hdcp, "r0p_available_dp"))
goto out;
out:
return status;
}
static enum mod_hdcp_status authenticated_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read");
mod_hdcp_execute_and_set(check_link_integrity_dp,
&input->link_integrity_check, &status,
hdcp, "link_integrity_check");
mod_hdcp_execute_and_set(check_no_reauthentication_request_dp,
&input->reauth_request_check, &status,
hdcp, "reauth_request_check");
out:
return status;
}
uint8_t mod_hdcp_execute_and_set(
mod_hdcp_action func, uint8_t *flag,
enum mod_hdcp_status *status, struct mod_hdcp *hdcp, char *str)
{
*status = func(hdcp);
if (*status == MOD_HDCP_STATUS_SUCCESS && *flag != PASS) {
HDCP_INPUT_PASS_TRACE(hdcp, str);
*flag = PASS;
} else if (*status != MOD_HDCP_STATUS_SUCCESS && *flag != FAIL) {
HDCP_INPUT_FAIL_TRACE(hdcp, str);
*flag = FAIL;
}
return (*status == MOD_HDCP_STATUS_SUCCESS);
}
enum mod_hdcp_status mod_hdcp_hdcp1_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case H1_A0_WAIT_FOR_ACTIVE_RX:
status = wait_for_active_rx(hdcp, event_ctx, input);
break;
case H1_A1_EXCHANGE_KSVS:
status = exchange_ksvs(hdcp, event_ctx, input);
break;
case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
status = computations_validate_rx_test_for_repeater(hdcp,
event_ctx, input);
break;
case H1_A45_AUTHENTICATED:
status = authenticated(hdcp, event_ctx, input);
break;
case H1_A8_WAIT_FOR_READY:
status = wait_for_ready(hdcp, event_ctx, input);
break;
case H1_A9_READ_KSV_LIST:
status = read_ksv_list(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp1_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
status = determine_rx_hdcp_capable_dp(hdcp, event_ctx, input);
break;
case D1_A1_EXCHANGE_KSVS:
status = exchange_ksvs(hdcp, event_ctx, input);
break;
case D1_A23_WAIT_FOR_R0_PRIME:
status = wait_for_r0_prime_dp(hdcp, event_ctx, input);
break;
case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
status = computations_validate_rx_test_for_repeater(
hdcp, event_ctx, input);
break;
case D1_A4_AUTHENTICATED:
status = authenticated_dp(hdcp, event_ctx, input);
break;
case D1_A6_WAIT_FOR_READY:
status = wait_for_ready(hdcp, event_ctx, input);
break;
case D1_A7_READ_KSV_LIST:
status = read_ksv_list(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
@@ -0,0 +1,322 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include "hdcp.h"
enum mod_hdcp_status mod_hdcp_hdcp1_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case H1_A0_WAIT_FOR_ACTIVE_RX:
if (input->bksv_read != PASS || input->bcaps_read != PASS) {
/* 1A-04: repeatedly attempts on port access failure */
callback_in_ms(500, output);
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A1_EXCHANGE_KSVS);
break;
case H1_A1_EXCHANGE_KSVS:
if (input->create_session != PASS) {
/* out of sync with psp state */
adjust->hdcp1.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->an_write != PASS ||
input->aksv_write != PASS ||
input->bksv_read != PASS ||
input->bksv_validation != PASS ||
input->ainfo_write == FAIL) {
/* 1A-05: consider invalid bksv a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(300, output);
set_state_id(hdcp, output,
H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER);
break;
case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
if (input->bcaps_read != PASS ||
input->r0p_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->rx_validation != PASS) {
/* 1A-06: consider invalid r0' a failure */
/* 1A-08: consider bksv listed in SRM a failure */
/*
* some slow RX will fail rx validation when it is
* not ready. give it more time to react before retry.
*/
fail_and_restart_in_ms(1000, &status, output);
break;
} else if (!conn->is_repeater && input->encryption != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_repeater) {
callback_in_ms(0, output);
set_watchdog_in_ms(hdcp, 5000, output);
set_state_id(hdcp, output, H1_A8_WAIT_FOR_READY);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A45_AUTHENTICATED);
set_auth_complete(hdcp, output);
}
break;
case H1_A45_AUTHENTICATED:
if (input->link_maintenance == FAIL) {
/* 1A-07: consider invalid ri' a failure */
/* 1A-07a: consider read ri' not returned a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(500, output);
increment_stay_counter(hdcp);
break;
case H1_A8_WAIT_FOR_READY:
if (input->ready_check != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-03: fail hdcp on ksv list READY timeout */
/* prevent black screen in next attempt */
adjust->hdcp1.postpone_encryption = 1;
fail_and_restart_in_ms(0, &status, output);
} else {
/* continue ksv list READY polling*/
callback_in_ms(500, output);
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A9_READ_KSV_LIST);
break;
case H1_A9_READ_KSV_LIST:
if (input->bstatus_read != PASS ||
input->max_cascade_check != PASS ||
input->max_devs_check != PASS ||
input->device_count_check != PASS ||
input->ksvlist_read != PASS ||
input->vp_read != PASS ||
input->ksvlist_vp_validation != PASS ||
input->encryption != PASS) {
/* 1B-06: consider MAX_CASCADE_EXCEEDED a failure */
/* 1B-05: consider MAX_DEVS_EXCEEDED a failure */
/* 1B-04: consider invalid v' a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A45_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp1_dp_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
if (input->bcaps_read != PASS) {
/* 1A-04: no authentication on bcaps read failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->hdcp_capable_dp != PASS) {
adjust->hdcp1.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A1_EXCHANGE_KSVS);
break;
case D1_A1_EXCHANGE_KSVS:
if (input->create_session != PASS) {
/* out of sync with psp state */
adjust->hdcp1.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->an_write != PASS ||
input->aksv_write != PASS ||
input->bksv_read != PASS ||
input->bksv_validation != PASS ||
input->ainfo_write == FAIL) {
/* 1A-05: consider invalid bksv a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 100, output);
set_state_id(hdcp, output, D1_A23_WAIT_FOR_R0_PRIME);
break;
case D1_A23_WAIT_FOR_R0_PRIME:
if (input->bstatus_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->r0p_available_dp != PASS) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER);
break;
case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
if (input->r0p_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->rx_validation != PASS) {
if (hdcp->state.stay_count < 2 &&
!hdcp->connection.is_hdcp1_revoked) {
/* allow 2 additional retries */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
} else {
/*
* 1A-06: consider invalid r0' a failure
* after 3 attempts.
* 1A-08: consider bksv listed in SRM a failure
*/
/*
* some slow RX will fail rx validation when it is
* not ready. give it more time to react before retry.
*/
fail_and_restart_in_ms(1000, &status, output);
}
break;
} else if ((!conn->is_repeater && input->encryption != PASS) ||
(!conn->is_repeater && is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (conn->hdcp1_retry_count < conn->link.adjust.hdcp1.min_auth_retries_wa) {
fail_and_restart_in_ms(200, &status, output);
break;
}
if (conn->is_repeater) {
set_watchdog_in_ms(hdcp, 5000, output);
set_state_id(hdcp, output, D1_A6_WAIT_FOR_READY);
} else {
set_state_id(hdcp, output, D1_A4_AUTHENTICATED);
set_auth_complete(hdcp, output);
}
break;
case D1_A4_AUTHENTICATED:
if (input->link_integrity_check == FAIL ||
input->reauth_request_check == FAIL) {
/* 1A-07: restart hdcp on a link integrity failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
break;
case D1_A6_WAIT_FOR_READY:
if (input->link_integrity_check == FAIL ||
input->reauth_request_check == FAIL) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ready_check != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-04: fail hdcp on ksv list READY timeout */
/* prevent black screen in next attempt */
adjust->hdcp1.postpone_encryption = 1;
fail_and_restart_in_ms(0, &status, output);
} else {
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A7_READ_KSV_LIST);
break;
case D1_A7_READ_KSV_LIST:
if (input->binfo_read_dp != PASS ||
input->max_cascade_check != PASS ||
input->max_devs_check != PASS) {
/* 1B-06: consider MAX_DEVS_EXCEEDED a failure */
/* 1B-07: consider MAX_CASCADE_EXCEEDED a failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->device_count_check != PASS) {
/*
* some slow dongle doesn't update
* device count as soon as downstream is connected.
* give it more time to react.
*/
adjust->hdcp1.postpone_encryption = 1;
fail_and_restart_in_ms(1000, &status, output);
break;
} else if (input->ksvlist_read != PASS ||
input->vp_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ksvlist_vp_validation != PASS) {
if (hdcp->state.stay_count < 2 &&
!hdcp->connection.is_hdcp1_revoked) {
/* allow 2 additional retries */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
} else {
/*
* 1B-05: consider invalid v' a failure
* after 3 attempts.
*/
fail_and_restart_in_ms(0, &status, output);
}
break;
} else if (input->encryption != PASS ||
(is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_state_id(hdcp, output, D1_A4_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
default:
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
@@ -0,0 +1,902 @@
/*
* Copyright 2018 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include <linux/delay.h>
#include "hdcp.h"
static inline uint16_t get_hdmi_rxstatus_msg_size(const uint8_t rxstatus[2])
{
return HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(rxstatus[1]) << 8 | rxstatus[0];
}
static inline enum mod_hdcp_status check_receiver_id_list_ready(struct mod_hdcp *hdcp)
{
uint8_t is_ready = 0;
if (is_dp_hdcp(hdcp))
is_ready = HDCP_2_2_DP_RXSTATUS_READY(hdcp->auth.msg.hdcp2.rxstatus_dp) ? 1 : 0;
else
is_ready = (HDCP_2_2_HDMI_RXSTATUS_READY(hdcp->auth.msg.hdcp2.rxstatus[1]) &&
get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus) != 0) ? 1 : 0;
return is_ready ? MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY;
}
static inline enum mod_hdcp_status check_hdcp2_capable(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
struct mod_hdcp_trace *trace = &hdcp->connection.trace;
if (is_dp_hdcp(hdcp))
status = (hdcp->auth.msg.hdcp2.rxcaps_dp[0] == HDCP_2_2_RX_CAPS_VERSION_VAL) &&
HDCP_2_2_DP_HDCP_CAPABLE(hdcp->auth.msg.hdcp2.rxcaps_dp[2]) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
else
status = (hdcp->auth.msg.hdcp2.hdcp2version_hdmi
& HDCP_2_2_HDMI_SUPPORT_MASK)
? MOD_HDCP_STATUS_SUCCESS
: MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
if (status == MOD_HDCP_STATUS_SUCCESS)
trace->hdcp2.attempt_count++;
return status;
}
static inline enum mod_hdcp_status check_reauthentication_request(
struct mod_hdcp *hdcp)
{
uint8_t ret = 0;
if (is_dp_hdcp(hdcp))
ret = HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST :
MOD_HDCP_STATUS_SUCCESS;
else
ret = HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(hdcp->auth.msg.hdcp2.rxstatus[1]) ?
MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST :
MOD_HDCP_STATUS_SUCCESS;
return ret;
}
static inline enum mod_hdcp_status check_link_integrity_failure_dp(
struct mod_hdcp *hdcp)
{
return HDCP_2_2_DP_RXSTATUS_LINK_FAILED(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static enum mod_hdcp_status check_ake_cert_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
status = MOD_HDCP_STATUS_SUCCESS;
} else {
status = mod_hdcp_read_rxstatus(hdcp);
if (status == MOD_HDCP_STATUS_SUCCESS) {
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_cert)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING;
}
}
return status;
}
static enum mod_hdcp_status check_h_prime_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
if (is_dp_hdcp(hdcp)) {
status = HDCP_2_2_DP_RXSTATUS_H_PRIME(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING;
} else {
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING;
}
out:
return status;
}
static enum mod_hdcp_status check_pairing_info_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
if (is_dp_hdcp(hdcp)) {
status = HDCP_2_2_DP_RXSTATUS_PAIRING(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING;
} else {
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING;
}
out:
return status;
}
static enum mod_hdcp_status poll_l_prime_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_FAILURE;
uint16_t max_wait = 20; // units of ms
uint16_t num_polls = 5;
uint16_t wait_time = max_wait / num_polls;
if (is_dp_hdcp(hdcp))
status = MOD_HDCP_STATUS_INVALID_OPERATION;
else
for (; num_polls; num_polls--) {
msleep(wait_time);
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
break;
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
status = (size == sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING;
if (status == MOD_HDCP_STATUS_SUCCESS)
break;
}
return status;
}
static enum mod_hdcp_status check_stream_ready_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
status = MOD_HDCP_STATUS_INVALID_OPERATION;
} else {
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
status = (size == sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING;
}
out:
return status;
}
static inline uint8_t get_device_count(struct mod_hdcp *hdcp)
{
return HDCP_2_2_DEV_COUNT_LO(hdcp->auth.msg.hdcp2.rx_id_list[2]) +
(HDCP_2_2_DEV_COUNT_HI(hdcp->auth.msg.hdcp2.rx_id_list[1]) << 4);
}
static enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp)
{
struct mod_hdcp_trace *trace = &hdcp->connection.trace;
/* Avoid device count == 0 to do authentication */
if (get_device_count(hdcp) == 0)
return MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE;
trace->hdcp2.downstream_device_count = get_device_count(hdcp);
trace->hdcp2.hdcp1_device_downstream =
HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp->auth.msg.hdcp2.rx_id_list[2]);
trace->hdcp2.hdcp2_legacy_device_downstream =
HDCP_2_2_HDCP_2_0_REP_CONNECTED(hdcp->auth.msg.hdcp2.rx_id_list[2]);
/* Some MST display may choose to report the internal panel as an HDCP RX. */
/* To update this condition with 1(because the immediate repeater's internal */
/* panel is possibly not included in DEVICE_COUNT) + get_device_count(hdcp). */
/* Device count must be greater than or equal to tracked hdcp displays. */
return ((1 + get_device_count(hdcp)) < get_active_display_count(hdcp)) ?
MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static uint8_t process_rxstatus(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
enum mod_hdcp_status *status)
{
if (!mod_hdcp_execute_and_set(mod_hdcp_read_rxstatus,
&input->rxstatus_read, status,
hdcp, "rxstatus_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_reauthentication_request,
&input->reauth_request_check, status,
hdcp, "reauth_request_check"))
goto out;
if (is_dp_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(check_link_integrity_failure_dp,
&input->link_integrity_check_dp, status,
hdcp, "link_integrity_check_dp"))
goto out;
}
if (hdcp->connection.is_repeater)
if (check_receiver_id_list_ready(hdcp) ==
MOD_HDCP_STATUS_SUCCESS) {
HDCP_INPUT_PASS_TRACE(hdcp, "rx_id_list_ready");
event_ctx->rx_id_list_ready = 1;
if (is_dp_hdcp(hdcp))
hdcp->auth.msg.hdcp2.rx_id_list_size =
sizeof(hdcp->auth.msg.hdcp2.rx_id_list);
else
hdcp->auth.msg.hdcp2.rx_id_list_size =
get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
}
out:
return (*status == MOD_HDCP_STATUS_SUCCESS);
}
static enum mod_hdcp_status known_hdcp2_capable_rx(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_hdcp2version,
&input->hdcp2version_read, &status,
hdcp, "hdcp2version_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_hdcp2_capable,
&input->hdcp2_capable_check, &status,
hdcp, "hdcp2_capable"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_ake_init(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_create_session,
&input->create_session, &status,
hdcp, "create_session"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_ake_init,
&input->ake_init_prepare, &status,
hdcp, "ake_init_prepare"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_ake_init,
&input->ake_init_write, &status,
hdcp, "ake_init_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status validate_ake_cert(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(check_ake_cert_available,
&input->ake_cert_available, &status,
hdcp, "ake_cert_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_ake_cert,
&input->ake_cert_read, &status,
hdcp, "ake_cert_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_ake_cert,
&input->ake_cert_validation, &status,
hdcp, "ake_cert_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_no_stored_km(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_write_no_stored_km,
&input->no_stored_km_write, &status,
hdcp, "no_stored_km_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status read_h_prime(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(check_h_prime_available,
&input->h_prime_available, &status,
hdcp, "h_prime_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_h_prime,
&input->h_prime_read, &status,
hdcp, "h_prime_read"))
goto out;
out:
return status;
}
static enum mod_hdcp_status read_pairing_info_and_validate_h_prime(
struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(check_pairing_info_available,
&input->pairing_available, &status,
hdcp, "pairing_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_pairing_info,
&input->pairing_info_read, &status,
hdcp, "pairing_info_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_h_prime,
&input->h_prime_validation, &status,
hdcp, "h_prime_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_stored_km(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_write_stored_km,
&input->stored_km_write, &status,
hdcp, "stored_km_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status validate_h_prime(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(check_h_prime_available,
&input->h_prime_available, &status,
hdcp, "h_prime_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_h_prime,
&input->h_prime_read, &status,
hdcp, "h_prime_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_h_prime,
&input->h_prime_validation, &status,
hdcp, "h_prime_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status locality_check(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_lc_init,
&input->lc_init_prepare, &status,
hdcp, "lc_init_prepare"))
goto out;
if (hdcp->connection.link.adjust.hdcp2.use_fw_locality_check) {
if (!mod_hdcp_execute_and_set(mod_hdcp_write_poll_read_lc_fw,
&input->l_prime_combo_read, &status,
hdcp, "l_prime_combo_read"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_write_lc_init,
&input->lc_init_write, &status,
hdcp, "lc_init_write"))
goto out;
if (is_dp_hdcp(hdcp))
msleep(16);
else
if (!mod_hdcp_execute_and_set(poll_l_prime_available,
&input->l_prime_available_poll, &status,
hdcp, "l_prime_available_poll"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_l_prime,
&input->l_prime_read, &status,
hdcp, "l_prime_read"))
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_l_prime,
&input->l_prime_validation, &status,
hdcp, "l_prime_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status exchange_ks_and_test_for_repeater(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_eks,
&input->eks_prepare, &status,
hdcp, "eks_prepare"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_eks,
&input->eks_write, &status,
hdcp, "eks_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status enable_encryption(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp)) {
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready)
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_enable_encryption,
&input->enable_encryption, &status,
hdcp, "enable_encryption"))
goto out;
if (is_dp_mst_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp2_enable_dp_stream_encryption,
&input->stream_encryption_dp, &status,
hdcp, "stream_encryption_dp"))
goto out;
}
out:
return status;
}
static enum mod_hdcp_status authenticated(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
process_rxstatus(hdcp, event_ctx, input, &status);
out:
return status;
}
static enum mod_hdcp_status wait_for_rx_id_list(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (!event_ctx->rx_id_list_ready) {
status = MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY;
goto out;
}
out:
return status;
}
static enum mod_hdcp_status verify_rx_id_list_and_send_ack(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_rx_id_list,
&input->rx_id_list_read,
&status, hdcp, "receiver_id_list_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_device_count,
&input->device_count_check,
&status, hdcp, "device_count_check"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_rx_id_list,
&input->rx_id_list_validation,
&status, hdcp, "rx_id_list_validation"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_repeater_auth_ack,
&input->repeater_auth_ack_write,
&status, hdcp, "repeater_auth_ack_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_stream_management(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp)) {
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready)
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_stream_management,
&input->prepare_stream_manage,
&status, hdcp, "prepare_stream_manage"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_stream_manage,
&input->stream_manage_write,
&status, hdcp, "stream_manage_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status validate_stream_ready(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp)) {
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready)
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(check_stream_ready_available,
&input->stream_ready_available,
&status, hdcp, "stream_ready_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_stream_ready,
&input->stream_ready_read,
&status, hdcp, "stream_ready_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_stream_ready,
&input->stream_ready_validation,
&status, hdcp, "stream_ready_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status determine_rx_hdcp_capable_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_rxcaps,
&input->rx_caps_read_dp,
&status, hdcp, "rx_caps_read_dp"))
goto out;
if (!mod_hdcp_execute_and_set(check_hdcp2_capable,
&input->hdcp2_capable_check, &status,
hdcp, "hdcp2_capable_check"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_content_stream_type_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_content_type,
&input->content_stream_type_write, &status,
hdcp, "content_stream_type_write"))
goto out;
out:
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp2_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case H2_A0_KNOWN_HDCP2_CAPABLE_RX:
status = known_hdcp2_capable_rx(hdcp, event_ctx, input);
break;
case H2_A1_SEND_AKE_INIT:
status = send_ake_init(hdcp, event_ctx, input);
break;
case H2_A1_VALIDATE_AKE_CERT:
status = validate_ake_cert(hdcp, event_ctx, input);
break;
case H2_A1_SEND_NO_STORED_KM:
status = send_no_stored_km(hdcp, event_ctx, input);
break;
case H2_A1_READ_H_PRIME:
status = read_h_prime(hdcp, event_ctx, input);
break;
case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
status = read_pairing_info_and_validate_h_prime(hdcp,
event_ctx, input);
break;
case H2_A1_SEND_STORED_KM:
status = send_stored_km(hdcp, event_ctx, input);
break;
case H2_A1_VALIDATE_H_PRIME:
status = validate_h_prime(hdcp, event_ctx, input);
break;
case H2_A2_LOCALITY_CHECK:
status = locality_check(hdcp, event_ctx, input);
break;
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
status = exchange_ks_and_test_for_repeater(hdcp, event_ctx, input);
break;
case H2_ENABLE_ENCRYPTION:
status = enable_encryption(hdcp, event_ctx, input);
break;
case H2_A5_AUTHENTICATED:
status = authenticated(hdcp, event_ctx, input);
break;
case H2_A6_WAIT_FOR_RX_ID_LIST:
status = wait_for_rx_id_list(hdcp, event_ctx, input);
break;
case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
status = verify_rx_id_list_and_send_ack(hdcp, event_ctx, input);
break;
case H2_A9_SEND_STREAM_MANAGEMENT:
status = send_stream_management(hdcp, event_ctx, input);
break;
case H2_A9_VALIDATE_STREAM_READY:
status = validate_stream_ready(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp2_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case D2_A0_DETERMINE_RX_HDCP_CAPABLE:
status = determine_rx_hdcp_capable_dp(hdcp, event_ctx, input);
break;
case D2_A1_SEND_AKE_INIT:
status = send_ake_init(hdcp, event_ctx, input);
break;
case D2_A1_VALIDATE_AKE_CERT:
status = validate_ake_cert(hdcp, event_ctx, input);
break;
case D2_A1_SEND_NO_STORED_KM:
status = send_no_stored_km(hdcp, event_ctx, input);
break;
case D2_A1_READ_H_PRIME:
status = read_h_prime(hdcp, event_ctx, input);
break;
case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
status = read_pairing_info_and_validate_h_prime(hdcp,
event_ctx, input);
break;
case D2_A1_SEND_STORED_KM:
status = send_stored_km(hdcp, event_ctx, input);
break;
case D2_A1_VALIDATE_H_PRIME:
status = validate_h_prime(hdcp, event_ctx, input);
break;
case D2_A2_LOCALITY_CHECK:
status = locality_check(hdcp, event_ctx, input);
break;
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
status = exchange_ks_and_test_for_repeater(hdcp,
event_ctx, input);
break;
case D2_SEND_CONTENT_STREAM_TYPE:
status = send_content_stream_type_dp(hdcp, event_ctx, input);
break;
case D2_ENABLE_ENCRYPTION:
status = enable_encryption(hdcp, event_ctx, input);
break;
case D2_A5_AUTHENTICATED:
status = authenticated(hdcp, event_ctx, input);
break;
case D2_A6_WAIT_FOR_RX_ID_LIST:
status = wait_for_rx_id_list(hdcp, event_ctx, input);
break;
case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
status = verify_rx_id_list_and_send_ack(hdcp, event_ctx, input);
break;
case D2_A9_SEND_STREAM_MANAGEMENT:
status = send_stream_management(hdcp, event_ctx, input);
break;
case D2_A9_VALIDATE_STREAM_READY:
status = validate_stream_ready(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
@@ -0,0 +1,704 @@
/*
* Copyright 2018 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include "hdcp.h"
enum mod_hdcp_status mod_hdcp_hdcp2_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case H2_A0_KNOWN_HDCP2_CAPABLE_RX:
if (input->hdcp2version_read != PASS ||
input->hdcp2_capable_check != PASS) {
adjust->hdcp2.disable = 1;
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_SEND_AKE_INIT);
}
break;
case H2_A1_SEND_AKE_INIT:
if (input->create_session != PASS ||
input->ake_init_prepare != PASS) {
/* out of sync with psp state */
adjust->hdcp2.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ake_init_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 100, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_VALIDATE_AKE_CERT);
break;
case H2_A1_VALIDATE_AKE_CERT:
if (input->ake_cert_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-08: consider ake timeout a failure */
/* some hdmi receivers are not ready for HDCP
* immediately after video becomes active,
* delay 1s before retry on first HDCP message
* timeout.
*/
fail_and_restart_in_ms(1000, &status, output);
} else {
/* continue ake cert polling*/
callback_in_ms(10, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->ake_cert_read != PASS ||
input->ake_cert_validation != PASS) {
/*
* 1A-09: consider invalid ake cert a failure
* 1A-10: consider receiver id listed in SRM a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_km_stored &&
!adjust->hdcp2.force_no_stored_km) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_SEND_STORED_KM);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_SEND_NO_STORED_KM);
}
break;
case H2_A1_SEND_NO_STORED_KM:
if (input->no_stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (adjust->hdcp2.increase_h_prime_timeout)
set_watchdog_in_ms(hdcp, 2000, output);
else
set_watchdog_in_ms(hdcp, 1000, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_READ_H_PRIME);
break;
case H2_A1_READ_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-11-3: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
} else {
/* continue h' polling */
callback_in_ms(100, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME);
break;
case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
if (input->pairing_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-12: consider pairing info timeout
* a failure
*/
fail_and_restart_in_ms(0, &status, output);
} else {
/* continue pairing info polling */
callback_in_ms(20, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->pairing_info_read != PASS ||
input->h_prime_validation != PASS) {
/* 1A-11-1: consider invalid h' a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A2_LOCALITY_CHECK);
break;
case H2_A1_SEND_STORED_KM:
if (input->stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_VALIDATE_H_PRIME);
break;
case H2_A1_VALIDATE_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-11-2: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
} else {
/* continue h' polling */
callback_in_ms(20, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->h_prime_validation != PASS) {
/* 1A-11-1: consider invalid h' a failure */
adjust->hdcp2.force_no_stored_km = 1;
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A2_LOCALITY_CHECK);
break;
case H2_A2_LOCALITY_CHECK:
/* 1A-05: consider disconnection after LC init a failure */
if (hdcp->state.stay_count > 10 ||
input->lc_init_prepare != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (adjust->hdcp2.use_fw_locality_check &&
input->l_prime_combo_read != PASS) {
/* 1A-13-2: consider l' timeout a failure */
if (adjust->hdcp2.use_sw_locality_fallback) {
/* switch to software locality check */
adjust->hdcp2.use_fw_locality_check = 0;
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!adjust->hdcp2.use_fw_locality_check &&
(input->lc_init_write != PASS ||
input->l_prime_available_poll != PASS ||
input->l_prime_read != PASS)) {
/* 1A-13-2: consider l' timeout a failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->l_prime_validation != PASS) {
/* 1A-13-1: consider invalid l' a failure */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER);
break;
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
if (input->eks_prepare != PASS ||
input->eks_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_repeater) {
set_watchdog_in_ms(hdcp, 3000, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A6_WAIT_FOR_RX_ID_LIST);
} else {
/* some CTS equipment requires a delay GREATER than
* 200 ms, so delay 210 ms instead of 200 ms
*/
callback_in_ms(210, output);
set_state_id(hdcp, output, H2_ENABLE_ENCRYPTION);
}
break;
case H2_ENABLE_ENCRYPTION:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
/*
* 1A-07: restart hdcp on REAUTH_REQ
* 1B-08: restart hdcp on REAUTH_REQ
*/
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->enable_encryption != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A5_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
case H2_A5_AUTHENTICATED:
if (input->rxstatus_read == FAIL ||
input->reauth_request_check == FAIL) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
}
callback_in_ms(500, output);
increment_stay_counter(hdcp);
break;
case H2_A6_WAIT_FOR_RX_ID_LIST:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!event_ctx->rx_id_list_ready) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-02: consider rx id list timeout a failure */
/* some CTS equipment's actual timeout
* measurement is slightly greater than 3000 ms.
* Delay 100 ms to ensure it is fully timeout
* before re-authentication.
*/
fail_and_restart_in_ms(100, &status, output);
} else {
callback_in_ms(300, output);
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->rx_id_list_read != PASS ||
input->device_count_check != PASS ||
input->rx_id_list_validation != PASS ||
input->repeater_auth_ack_write != PASS) {
/* 1B-03: consider invalid v' a failure
* 1B-04: consider MAX_DEVS_EXCEEDED a failure
* 1B-05: consider MAX_CASCADE_EXCEEDED a failure
* 1B-06: consider invalid seq_num_V a failure
* 1B-09: consider seq_num_V rollover a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_SEND_STREAM_MANAGEMENT);
break;
case H2_A9_SEND_STREAM_MANAGEMENT:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->prepare_stream_manage != PASS ||
input->stream_manage_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 100, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_VALIDATE_STREAM_READY);
break;
case H2_A9_VALIDATE_STREAM_READY:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->stream_ready_available != PASS) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-10-2: restart content stream management on
* stream ready timeout
*/
hdcp->auth.count.stream_management_retry_count++;
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_SEND_STREAM_MANAGEMENT);
} else {
callback_in_ms(10, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->stream_ready_read != PASS ||
input->stream_ready_validation != PASS) {
/*
* 1B-10-1: restart content stream management
* on invalid M'
*/
if (hdcp->auth.count.stream_management_retry_count > 10) {
fail_and_restart_in_ms(0, &status, output);
} else {
hdcp->auth.count.stream_management_retry_count++;
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_SEND_STREAM_MANAGEMENT);
}
break;
}
callback_in_ms(200, output);
set_state_id(hdcp, output, H2_ENABLE_ENCRYPTION);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp2_dp_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case D2_A0_DETERMINE_RX_HDCP_CAPABLE:
if (input->rx_caps_read_dp != PASS ||
input->hdcp2_capable_check != PASS) {
adjust->hdcp2.disable = 1;
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A1_SEND_AKE_INIT);
}
break;
case D2_A1_SEND_AKE_INIT:
if (input->create_session != PASS ||
input->ake_init_prepare != PASS) {
/* out of sync with psp state */
adjust->hdcp2.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ake_init_write != PASS) {
/* possibly display not ready */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(100, output);
set_state_id(hdcp, output, D2_A1_VALIDATE_AKE_CERT);
break;
case D2_A1_VALIDATE_AKE_CERT:
if (input->ake_cert_read != PASS ||
input->ake_cert_validation != PASS) {
/*
* 1A-08: consider invalid ake cert a failure
* 1A-09: consider receiver id listed in SRM a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_km_stored &&
!adjust->hdcp2.force_no_stored_km) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A1_SEND_STORED_KM);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A1_SEND_NO_STORED_KM);
}
break;
case D2_A1_SEND_NO_STORED_KM:
if (input->no_stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (adjust->hdcp2.increase_h_prime_timeout)
set_watchdog_in_ms(hdcp, 2000, output);
else
set_watchdog_in_ms(hdcp, 1000, output);
set_state_id(hdcp, output, D2_A1_READ_H_PRIME);
break;
case D2_A1_READ_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/* 1A-10-3: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
else
increment_stay_counter(hdcp);
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
set_state_id(hdcp, output, D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME);
break;
case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
if (input->pairing_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/*
* 1A-11: consider pairing info timeout
* a failure
*/
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
} else if (input->pairing_info_read != PASS ||
input->h_prime_validation != PASS) {
/* 1A-10-1: consider invalid h' a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A2_LOCALITY_CHECK);
break;
case D2_A1_SEND_STORED_KM:
if (input->stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
set_state_id(hdcp, output, D2_A1_VALIDATE_H_PRIME);
break;
case D2_A1_VALIDATE_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/* 1A-10-2: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
else
increment_stay_counter(hdcp);
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->h_prime_validation != PASS) {
/* 1A-10-1: consider invalid h' a failure */
adjust->hdcp2.force_no_stored_km = 1;
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A2_LOCALITY_CHECK);
break;
case D2_A2_LOCALITY_CHECK:
if (hdcp->state.stay_count > 10 ||
input->lc_init_prepare != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (adjust->hdcp2.use_fw_locality_check &&
input->l_prime_combo_read != PASS) {
if (adjust->hdcp2.use_sw_locality_fallback) {
/* switch to software locality check */
adjust->hdcp2.use_fw_locality_check = 0;
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!adjust->hdcp2.use_fw_locality_check &&
(input->lc_init_write != PASS ||
input->l_prime_read != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->l_prime_validation != PASS) {
/* 1A-12: consider invalid l' a failure */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER);
break;
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
if (input->eks_prepare != PASS ||
input->eks_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_repeater) {
set_watchdog_in_ms(hdcp, 3000, output);
set_state_id(hdcp, output, D2_A6_WAIT_FOR_RX_ID_LIST);
} else {
callback_in_ms(1, output);
set_state_id(hdcp, output, D2_SEND_CONTENT_STREAM_TYPE);
}
break;
case D2_SEND_CONTENT_STREAM_TYPE:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS ||
input->content_stream_type_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(210, output);
set_state_id(hdcp, output, D2_ENABLE_ENCRYPTION);
break;
case D2_ENABLE_ENCRYPTION:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
/*
* 1A-07: restart hdcp on REAUTH_REQ
* 1B-08: restart hdcp on REAUTH_REQ
*/
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->enable_encryption != PASS ||
(is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_state_id(hdcp, output, D2_A5_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
case D2_A5_AUTHENTICATED:
if (input->rxstatus_read == FAIL ||
input->reauth_request_check == FAIL) {
fail_and_restart_in_ms(100, &status, output);
break;
} else if (input->link_integrity_check_dp == FAIL) {
if (hdcp->connection.hdcp2_retry_count >= 1)
adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
}
increment_stay_counter(hdcp);
break;
case D2_A6_WAIT_FOR_RX_ID_LIST:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!event_ctx->rx_id_list_ready) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/* 1B-02: consider rx id list timeout a failure */
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS ||
input->rx_id_list_read != PASS ||
input->device_count_check != PASS ||
input->rx_id_list_validation != PASS ||
input->repeater_auth_ack_write != PASS) {
/*
* 1B-03: consider invalid v' a failure
* 1B-04: consider MAX_DEVS_EXCEEDED a failure
* 1B-05: consider MAX_CASCADE_EXCEEDED a failure
* 1B-06: consider invalid seq_num_V a failure
* 1B-09: consider seq_num_V rollover a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A9_SEND_STREAM_MANAGEMENT);
break;
case D2_A9_SEND_STREAM_MANAGEMENT:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->prepare_stream_manage != PASS ||
input->stream_manage_write != PASS) {
if (event_ctx->event == MOD_HDCP_EVENT_CALLBACK)
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
}
callback_in_ms(100, output);
set_state_id(hdcp, output, D2_A9_VALIDATE_STREAM_READY);
break;
case D2_A9_VALIDATE_STREAM_READY:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->stream_ready_read != PASS ||
input->stream_ready_validation != PASS) {
/*
* 1B-10-1: restart content stream management
* on invalid M'
* 1B-10-2: consider stream ready timeout a failure
*/
if (hdcp->auth.count.stream_management_retry_count > 10) {
fail_and_restart_in_ms(0, &status, output);
} else if (event_ctx->event == MOD_HDCP_EVENT_CALLBACK) {
hdcp->auth.count.stream_management_retry_count++;
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A9_SEND_STREAM_MANAGEMENT);
} else {
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(200, output);
set_state_id(hdcp, output, D2_ENABLE_ENCRYPTION);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
@@ -0,0 +1,764 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include "hdcp.h"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define HDCP_I2C_ADDR 0x3a /* 0x74 >> 1*/
#define KSV_READ_SIZE 0xf /* 0x6803b - 0x6802c */
#define HDCP_MAX_AUX_TRANSACTION_SIZE 16
#define DP_CP_IRQ (1 << 2)
enum mod_hdcp_ddc_message_id {
MOD_HDCP_MESSAGE_ID_INVALID = -1,
/* HDCP 1.4 */
MOD_HDCP_MESSAGE_ID_READ_BKSV = 0,
MOD_HDCP_MESSAGE_ID_READ_RI_R0,
MOD_HDCP_MESSAGE_ID_WRITE_AKSV,
MOD_HDCP_MESSAGE_ID_WRITE_AINFO,
MOD_HDCP_MESSAGE_ID_WRITE_AN,
MOD_HDCP_MESSAGE_ID_READ_VH_X,
MOD_HDCP_MESSAGE_ID_READ_VH_0,
MOD_HDCP_MESSAGE_ID_READ_VH_1,
MOD_HDCP_MESSAGE_ID_READ_VH_2,
MOD_HDCP_MESSAGE_ID_READ_VH_3,
MOD_HDCP_MESSAGE_ID_READ_VH_4,
MOD_HDCP_MESSAGE_ID_READ_BCAPS,
MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
MOD_HDCP_MESSAGE_ID_READ_BINFO,
/* HDCP 2.2 */
MOD_HDCP_MESSAGE_ID_HDCP2VERSION,
MOD_HDCP_MESSAGE_ID_RX_CAPS,
MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE,
MOD_HDCP_MESSAGE_ID_MAX
};
static const uint8_t hdcp_i2c_offsets[] = {
[MOD_HDCP_MESSAGE_ID_READ_BKSV] = 0x0,
[MOD_HDCP_MESSAGE_ID_READ_RI_R0] = 0x8,
[MOD_HDCP_MESSAGE_ID_WRITE_AKSV] = 0x10,
[MOD_HDCP_MESSAGE_ID_WRITE_AINFO] = 0x15,
[MOD_HDCP_MESSAGE_ID_WRITE_AN] = 0x18,
[MOD_HDCP_MESSAGE_ID_READ_VH_X] = 0x20,
[MOD_HDCP_MESSAGE_ID_READ_VH_0] = 0x20,
[MOD_HDCP_MESSAGE_ID_READ_VH_1] = 0x24,
[MOD_HDCP_MESSAGE_ID_READ_VH_2] = 0x28,
[MOD_HDCP_MESSAGE_ID_READ_VH_3] = 0x2C,
[MOD_HDCP_MESSAGE_ID_READ_VH_4] = 0x30,
[MOD_HDCP_MESSAGE_ID_READ_BCAPS] = 0x40,
[MOD_HDCP_MESSAGE_ID_READ_BSTATUS] = 0x41,
[MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x43,
[MOD_HDCP_MESSAGE_ID_READ_BINFO] = 0xFF,
[MOD_HDCP_MESSAGE_ID_HDCP2VERSION] = 0x50,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x60,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x80,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x80,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x60,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x80,
[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x70,
[MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x0
};
static const uint32_t hdcp_dpcd_addrs[] = {
[MOD_HDCP_MESSAGE_ID_READ_BKSV] = 0x68000,
[MOD_HDCP_MESSAGE_ID_READ_RI_R0] = 0x68005,
[MOD_HDCP_MESSAGE_ID_WRITE_AKSV] = 0x68007,
[MOD_HDCP_MESSAGE_ID_WRITE_AINFO] = 0x6803B,
[MOD_HDCP_MESSAGE_ID_WRITE_AN] = 0x6800c,
[MOD_HDCP_MESSAGE_ID_READ_VH_X] = 0x68014,
[MOD_HDCP_MESSAGE_ID_READ_VH_0] = 0x68014,
[MOD_HDCP_MESSAGE_ID_READ_VH_1] = 0x68018,
[MOD_HDCP_MESSAGE_ID_READ_VH_2] = 0x6801c,
[MOD_HDCP_MESSAGE_ID_READ_VH_3] = 0x68020,
[MOD_HDCP_MESSAGE_ID_READ_VH_4] = 0x68024,
[MOD_HDCP_MESSAGE_ID_READ_BCAPS] = 0x68028,
[MOD_HDCP_MESSAGE_ID_READ_BSTATUS] = 0x68029,
[MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x6802c,
[MOD_HDCP_MESSAGE_ID_READ_BINFO] = 0x6802a,
[MOD_HDCP_MESSAGE_ID_RX_CAPS] = 0x6921d,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x69000,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x6900b,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x69220,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x692a0,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x692c0,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x692e0,
[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x692f0,
[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x692f8,
[MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x69318,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x69330,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2] = 0x69340,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x693e0,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x693f0,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x69473,
[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x69493,
[MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x69494
};
static enum mod_hdcp_status read(struct mod_hdcp *hdcp,
enum mod_hdcp_ddc_message_id msg_id,
uint8_t *buf,
uint32_t buf_len)
{
bool success = true;
uint32_t cur_size = 0;
uint32_t data_offset = 0;
if (msg_id == MOD_HDCP_MESSAGE_ID_INVALID ||
msg_id >= MOD_HDCP_MESSAGE_ID_MAX)
return MOD_HDCP_STATUS_DDC_FAILURE;
if (is_dp_hdcp(hdcp)) {
int num_dpcd_addrs = ARRAY_SIZE(hdcp_dpcd_addrs);
if (msg_id >= num_dpcd_addrs)
return MOD_HDCP_STATUS_DDC_FAILURE;
while (buf_len > 0) {
cur_size = MIN(buf_len, HDCP_MAX_AUX_TRANSACTION_SIZE);
success = hdcp->config.ddc.funcs.read_dpcd(hdcp->config.ddc.handle,
hdcp_dpcd_addrs[msg_id] + data_offset,
buf + data_offset,
cur_size);
if (!success)
break;
buf_len -= cur_size;
data_offset += cur_size;
}
} else {
int num_i2c_offsets = ARRAY_SIZE(hdcp_i2c_offsets);
if (msg_id >= num_i2c_offsets)
return MOD_HDCP_STATUS_DDC_FAILURE;
success = hdcp->config.ddc.funcs.read_i2c(
hdcp->config.ddc.handle,
HDCP_I2C_ADDR,
hdcp_i2c_offsets[msg_id],
buf,
(uint32_t)buf_len);
}
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
}
static enum mod_hdcp_status read_repeatedly(struct mod_hdcp *hdcp,
enum mod_hdcp_ddc_message_id msg_id,
uint8_t *buf,
uint32_t buf_len,
uint8_t read_size)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_DDC_FAILURE;
uint32_t cur_size = 0;
uint32_t data_offset = 0;
while (buf_len > 0) {
cur_size = MIN(buf_len, read_size);
status = read(hdcp, msg_id, buf + data_offset, cur_size);
if (status != MOD_HDCP_STATUS_SUCCESS)
break;
buf_len -= cur_size;
data_offset += cur_size;
}
return status;
}
static enum mod_hdcp_status write(struct mod_hdcp *hdcp,
enum mod_hdcp_ddc_message_id msg_id,
uint8_t *buf,
uint32_t buf_len)
{
bool success = true;
uint32_t cur_size = 0;
uint32_t data_offset = 0;
if (msg_id == MOD_HDCP_MESSAGE_ID_INVALID ||
msg_id >= MOD_HDCP_MESSAGE_ID_MAX)
return MOD_HDCP_STATUS_DDC_FAILURE;
if (is_dp_hdcp(hdcp)) {
int num_dpcd_addrs = ARRAY_SIZE(hdcp_dpcd_addrs);
if (msg_id >= num_dpcd_addrs)
return MOD_HDCP_STATUS_DDC_FAILURE;
while (buf_len > 0) {
cur_size = MIN(buf_len, HDCP_MAX_AUX_TRANSACTION_SIZE);
success = hdcp->config.ddc.funcs.write_dpcd(
hdcp->config.ddc.handle,
hdcp_dpcd_addrs[msg_id] + data_offset,
buf + data_offset,
cur_size);
if (!success)
break;
buf_len -= cur_size;
data_offset += cur_size;
}
} else {
int num_i2c_offsets = ARRAY_SIZE(hdcp_i2c_offsets);
if (msg_id >= num_i2c_offsets)
return MOD_HDCP_STATUS_DDC_FAILURE;
hdcp->buf[0] = hdcp_i2c_offsets[msg_id];
memmove(&hdcp->buf[1], buf, buf_len);
success = hdcp->config.ddc.funcs.write_i2c(
hdcp->config.ddc.handle,
HDCP_I2C_ADDR,
hdcp->buf,
(uint32_t)(buf_len+1));
}
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
}
enum mod_hdcp_status mod_hdcp_read_bksv(struct mod_hdcp *hdcp)
{
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BKSV,
hdcp->auth.msg.hdcp1.bksv,
sizeof(hdcp->auth.msg.hdcp1.bksv));
}
enum mod_hdcp_status mod_hdcp_read_bcaps(struct mod_hdcp *hdcp)
{
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BCAPS,
&hdcp->auth.msg.hdcp1.bcaps,
sizeof(hdcp->auth.msg.hdcp1.bcaps));
}
enum mod_hdcp_status mod_hdcp_read_bstatus(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
1);
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
sizeof(hdcp->auth.msg.hdcp1.bstatus));
return status;
}
enum mod_hdcp_status mod_hdcp_read_r0p(struct mod_hdcp *hdcp)
{
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RI_R0,
(uint8_t *)&hdcp->auth.msg.hdcp1.r0p,
sizeof(hdcp->auth.msg.hdcp1.r0p));
}
/* special case, reading repeatedly at the same address, don't use read() */
enum mod_hdcp_status mod_hdcp_read_ksvlist(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = read_repeatedly(hdcp, MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
hdcp->auth.msg.hdcp1.ksvlist,
hdcp->auth.msg.hdcp1.ksvlist_size,
KSV_READ_SIZE);
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
(uint8_t *)&hdcp->auth.msg.hdcp1.ksvlist,
hdcp->auth.msg.hdcp1.ksvlist_size);
return status;
}
enum mod_hdcp_status mod_hdcp_read_vp(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_0,
&hdcp->auth.msg.hdcp1.vp[0], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_1,
&hdcp->auth.msg.hdcp1.vp[4], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_2,
&hdcp->auth.msg.hdcp1.vp[8], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_3,
&hdcp->auth.msg.hdcp1.vp[12], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_4,
&hdcp->auth.msg.hdcp1.vp[16], 4);
out:
return status;
}
enum mod_hdcp_status mod_hdcp_read_binfo(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BINFO,
(uint8_t *)&hdcp->auth.msg.hdcp1.binfo_dp,
sizeof(hdcp->auth.msg.hdcp1.binfo_dp));
else
status = MOD_HDCP_STATUS_INVALID_OPERATION;
return status;
}
enum mod_hdcp_status mod_hdcp_write_aksv(struct mod_hdcp *hdcp)
{
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKSV,
hdcp->auth.msg.hdcp1.aksv,
sizeof(hdcp->auth.msg.hdcp1.aksv));
}
enum mod_hdcp_status mod_hdcp_write_ainfo(struct mod_hdcp *hdcp)
{
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AINFO,
&hdcp->auth.msg.hdcp1.ainfo,
sizeof(hdcp->auth.msg.hdcp1.ainfo));
}
enum mod_hdcp_status mod_hdcp_write_an(struct mod_hdcp *hdcp)
{
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AN,
hdcp->auth.msg.hdcp1.an,
sizeof(hdcp->auth.msg.hdcp1.an));
}
enum mod_hdcp_status mod_hdcp_read_hdcp2version(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = MOD_HDCP_STATUS_INVALID_OPERATION;
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_HDCP2VERSION,
&hdcp->auth.msg.hdcp2.hdcp2version_hdmi,
sizeof(hdcp->auth.msg.hdcp2.hdcp2version_hdmi));
return status;
}
enum mod_hdcp_status mod_hdcp_read_rxcaps(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (!is_dp_hdcp(hdcp))
status = MOD_HDCP_STATUS_INVALID_OPERATION;
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_RX_CAPS,
hdcp->auth.msg.hdcp2.rxcaps_dp,
sizeof(hdcp->auth.msg.hdcp2.rxcaps_dp));
return status;
}
enum mod_hdcp_status mod_hdcp_read_rxstatus(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
&hdcp->auth.msg.hdcp2.rxstatus_dp,
1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
(uint8_t *)&hdcp->auth.msg.hdcp2.rxstatus,
sizeof(hdcp->auth.msg.hdcp2.rxstatus));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_ake_cert(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.ake_cert[0] = HDCP_2_2_AKE_SEND_CERT;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
hdcp->auth.msg.hdcp2.ake_cert+1,
sizeof(hdcp->auth.msg.hdcp2.ake_cert)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
hdcp->auth.msg.hdcp2.ake_cert,
sizeof(hdcp->auth.msg.hdcp2.ake_cert));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_h_prime(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.ake_h_prime[0] = HDCP_2_2_AKE_SEND_HPRIME;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
hdcp->auth.msg.hdcp2.ake_h_prime+1,
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
hdcp->auth.msg.hdcp2.ake_h_prime,
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_pairing_info(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.ake_pairing_info[0] = HDCP_2_2_AKE_SEND_PAIRING_INFO;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
hdcp->auth.msg.hdcp2.ake_pairing_info+1,
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
hdcp->auth.msg.hdcp2.ake_pairing_info,
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_l_prime(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.lc_l_prime[0] = HDCP_2_2_LC_SEND_LPRIME;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
hdcp->auth.msg.hdcp2.lc_l_prime+1,
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
hdcp->auth.msg.hdcp2.lc_l_prime,
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (is_dp_hdcp(hdcp)) {
uint32_t device_count = 0;
uint32_t rx_id_list_size = 0;
uint32_t bytes_read = 0;
hdcp->auth.msg.hdcp2.rx_id_list[0] = HDCP_2_2_REP_SEND_RECVID_LIST;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
hdcp->auth.msg.hdcp2.rx_id_list+1,
HDCP_MAX_AUX_TRANSACTION_SIZE);
if (status == MOD_HDCP_STATUS_SUCCESS) {
bytes_read = HDCP_MAX_AUX_TRANSACTION_SIZE;
device_count = HDCP_2_2_DEV_COUNT_LO(hdcp->auth.msg.hdcp2.rx_id_list[2]) +
(HDCP_2_2_DEV_COUNT_HI(hdcp->auth.msg.hdcp2.rx_id_list[1]) << 4);
rx_id_list_size = MIN((21 + 5 * device_count),
(sizeof(hdcp->auth.msg.hdcp2.rx_id_list) - 1));
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2,
hdcp->auth.msg.hdcp2.rx_id_list + 1 + bytes_read,
(rx_id_list_size - 1) / HDCP_MAX_AUX_TRANSACTION_SIZE * HDCP_MAX_AUX_TRANSACTION_SIZE);
}
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
hdcp->auth.msg.hdcp2.rx_id_list,
MIN(hdcp->auth.msg.hdcp2.rx_id_list_size,
sizeof(hdcp->auth.msg.hdcp2.rx_id_list)));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_stream_ready(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready[0] = HDCP_2_2_REP_STREAM_READY;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready+1,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready));
}
return status;
}
enum mod_hdcp_status mod_hdcp_write_ake_init(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
hdcp->auth.msg.hdcp2.ake_init+1,
sizeof(hdcp->auth.msg.hdcp2.ake_init)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
hdcp->auth.msg.hdcp2.ake_init,
sizeof(hdcp->auth.msg.hdcp2.ake_init));
return status;
}
enum mod_hdcp_status mod_hdcp_write_no_stored_km(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
hdcp->auth.msg.hdcp2.ake_no_stored_km+1,
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
hdcp->auth.msg.hdcp2.ake_no_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km));
return status;
}
enum mod_hdcp_status mod_hdcp_write_stored_km(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
hdcp->auth.msg.hdcp2.ake_stored_km+1,
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
hdcp->auth.msg.hdcp2.ake_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km));
return status;
}
enum mod_hdcp_status mod_hdcp_write_lc_init(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
hdcp->auth.msg.hdcp2.lc_init+1,
sizeof(hdcp->auth.msg.hdcp2.lc_init)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
hdcp->auth.msg.hdcp2.lc_init,
sizeof(hdcp->auth.msg.hdcp2.lc_init));
return status;
}
enum mod_hdcp_status mod_hdcp_write_eks(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
hdcp->auth.msg.hdcp2.ske_eks+1,
sizeof(hdcp->auth.msg.hdcp2.ske_eks)-1);
else
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
hdcp->auth.msg.hdcp2.ske_eks,
sizeof(hdcp->auth.msg.hdcp2.ske_eks));
return status;
}
enum mod_hdcp_status mod_hdcp_write_repeater_auth_ack(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
hdcp->auth.msg.hdcp2.repeater_auth_ack+1,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
hdcp->auth.msg.hdcp2.repeater_auth_ack,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack));
return status;
}
enum mod_hdcp_status mod_hdcp_write_stream_manage(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage+1,
hdcp->auth.msg.hdcp2.stream_manage_size-1);
else
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage,
hdcp->auth.msg.hdcp2.stream_manage_size);
return status;
}
enum mod_hdcp_status mod_hdcp_write_content_type(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE,
hdcp->auth.msg.hdcp2.content_stream_type_dp+1,
sizeof(hdcp->auth.msg.hdcp2.content_stream_type_dp)-1);
else
status = MOD_HDCP_STATUS_INVALID_OPERATION;
return status;
}
enum mod_hdcp_status mod_hdcp_clear_cp_irq_status(struct mod_hdcp *hdcp)
{
uint8_t clear_cp_irq_bit = DP_CP_IRQ;
uint32_t size = 1;
if (is_dp_hdcp(hdcp)) {
uint32_t cp_irq_addrs = (hdcp->connection.link.dp.rev >= 0x14)
? DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0:DP_DEVICE_SERVICE_IRQ_VECTOR;
return hdcp->config.ddc.funcs.write_dpcd(hdcp->config.ddc.handle, cp_irq_addrs,
&clear_cp_irq_bit, size) ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
}
return MOD_HDCP_STATUS_INVALID_OPERATION;
}
static bool write_stall_read_lc_fw_aux(struct mod_hdcp *hdcp)
{
struct mod_hdcp_message_hdcp2 *hdcp2 = &hdcp->auth.msg.hdcp2;
struct mod_hdcp_atomic_op_aux write = {
hdcp_dpcd_addrs[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT],
hdcp2->lc_init + 1,
sizeof(hdcp2->lc_init) - 1,
};
struct mod_hdcp_atomic_op_aux stall = { 0, NULL, 0, };
struct mod_hdcp_atomic_op_aux read = {
hdcp_dpcd_addrs[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME],
hdcp2->lc_l_prime + 1,
sizeof(hdcp2->lc_l_prime) - 1,
};
hdcp2->lc_l_prime[0] = HDCP_2_2_LC_SEND_LPRIME;
return hdcp->config.ddc.funcs.atomic_write_poll_read_aux(
hdcp->config.ddc.handle,
&write,
&stall,
&read,
16 * 1000,
0
);
}
static bool write_poll_read_lc_fw_i2c(struct mod_hdcp *hdcp)
{
struct mod_hdcp_message_hdcp2 *hdcp2 = &hdcp->auth.msg.hdcp2;
uint8_t expected_rxstatus[2] = { sizeof(hdcp2->lc_l_prime) };
hdcp->buf[0] = hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT];
memmove(&hdcp->buf[1], hdcp2->lc_init, sizeof(hdcp2->lc_init));
struct mod_hdcp_atomic_op_i2c write = {
HDCP_I2C_ADDR,
0,
hdcp->buf,
sizeof(hdcp2->lc_init) + 1,
};
struct mod_hdcp_atomic_op_i2c poll = {
HDCP_I2C_ADDR,
hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS],
expected_rxstatus,
sizeof(expected_rxstatus),
};
struct mod_hdcp_atomic_op_i2c read = {
HDCP_I2C_ADDR,
hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME],
hdcp2->lc_l_prime,
sizeof(hdcp2->lc_l_prime),
};
return hdcp->config.ddc.funcs.atomic_write_poll_read_i2c(
hdcp->config.ddc.handle,
&write,
&poll,
&read,
20 * 1000,
6
);
}
enum mod_hdcp_status mod_hdcp_write_poll_read_lc_fw(struct mod_hdcp *hdcp)
{
const bool success = (is_dp_hdcp(hdcp) ? write_stall_read_lc_fw_aux : write_poll_read_lc_fw_i2c)(hdcp);
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_HDCP2_LOCALITY_COMBO_READ_FAILURE;
}
@@ -0,0 +1,249 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include "hdcp.h"
void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size,
uint8_t *buf, uint32_t buf_size)
{
const uint8_t bytes_per_line = 16,
byte_size = 3,
newline_size = 1,
terminator_size = 1;
uint32_t buf_pos = 0;
uint32_t i = 0;
/* Need room for at least the terminator. */
if (buf_size < terminator_size)
return;
for (i = 0; i < msg_size; i++) {
uint32_t needed = byte_size + terminator_size;
if (i % bytes_per_line == 0)
needed += newline_size;
if (buf_pos + needed > buf_size)
break;
if (i % bytes_per_line == 0)
buf[buf_pos++] = '\n';
sprintf((char *)&buf[buf_pos], "%02X ", msg[i]);
buf_pos += byte_size;
}
buf[buf_pos++] = '\0';
}
void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp)
{
if (is_hdcp1(hdcp)) {
HDCP_DDC_READ_TRACE(hdcp, "BKSV", hdcp->auth.msg.hdcp1.bksv,
sizeof(hdcp->auth.msg.hdcp1.bksv));
HDCP_DDC_READ_TRACE(hdcp, "BCAPS", &hdcp->auth.msg.hdcp1.bcaps,
sizeof(hdcp->auth.msg.hdcp1.bcaps));
HDCP_DDC_READ_TRACE(hdcp, "BSTATUS",
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
sizeof(hdcp->auth.msg.hdcp1.bstatus));
HDCP_DDC_WRITE_TRACE(hdcp, "AN", hdcp->auth.msg.hdcp1.an,
sizeof(hdcp->auth.msg.hdcp1.an));
HDCP_DDC_WRITE_TRACE(hdcp, "AKSV", hdcp->auth.msg.hdcp1.aksv,
sizeof(hdcp->auth.msg.hdcp1.aksv));
HDCP_DDC_WRITE_TRACE(hdcp, "AINFO", &hdcp->auth.msg.hdcp1.ainfo,
sizeof(hdcp->auth.msg.hdcp1.ainfo));
HDCP_DDC_READ_TRACE(hdcp, "RI' / R0'",
(uint8_t *)&hdcp->auth.msg.hdcp1.r0p,
sizeof(hdcp->auth.msg.hdcp1.r0p));
HDCP_DDC_READ_TRACE(hdcp, "BINFO",
(uint8_t *)&hdcp->auth.msg.hdcp1.binfo_dp,
sizeof(hdcp->auth.msg.hdcp1.binfo_dp));
HDCP_DDC_READ_TRACE(hdcp, "KSVLIST", hdcp->auth.msg.hdcp1.ksvlist,
hdcp->auth.msg.hdcp1.ksvlist_size);
HDCP_DDC_READ_TRACE(hdcp, "V'", hdcp->auth.msg.hdcp1.vp,
sizeof(hdcp->auth.msg.hdcp1.vp));
} else if (is_hdcp2(hdcp)) {
HDCP_DDC_READ_TRACE(hdcp, "HDCP2Version",
&hdcp->auth.msg.hdcp2.hdcp2version_hdmi,
sizeof(hdcp->auth.msg.hdcp2.hdcp2version_hdmi));
HDCP_DDC_READ_TRACE(hdcp, "Rx Caps", hdcp->auth.msg.hdcp2.rxcaps_dp,
sizeof(hdcp->auth.msg.hdcp2.rxcaps_dp));
HDCP_DDC_WRITE_TRACE(hdcp, "AKE Init", hdcp->auth.msg.hdcp2.ake_init,
sizeof(hdcp->auth.msg.hdcp2.ake_init));
HDCP_DDC_READ_TRACE(hdcp, "AKE Cert", hdcp->auth.msg.hdcp2.ake_cert,
sizeof(hdcp->auth.msg.hdcp2.ake_cert));
HDCP_DDC_WRITE_TRACE(hdcp, "Stored KM",
hdcp->auth.msg.hdcp2.ake_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km));
HDCP_DDC_WRITE_TRACE(hdcp, "No Stored KM",
hdcp->auth.msg.hdcp2.ake_no_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km));
HDCP_DDC_READ_TRACE(hdcp, "H'", hdcp->auth.msg.hdcp2.ake_h_prime,
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime));
HDCP_DDC_READ_TRACE(hdcp, "Pairing Info",
hdcp->auth.msg.hdcp2.ake_pairing_info,
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info));
HDCP_DDC_WRITE_TRACE(hdcp, "LC Init", hdcp->auth.msg.hdcp2.lc_init,
sizeof(hdcp->auth.msg.hdcp2.lc_init));
HDCP_DDC_READ_TRACE(hdcp, "L'", hdcp->auth.msg.hdcp2.lc_l_prime,
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime));
HDCP_DDC_WRITE_TRACE(hdcp, "Exchange KS", hdcp->auth.msg.hdcp2.ske_eks,
sizeof(hdcp->auth.msg.hdcp2.ske_eks));
HDCP_DDC_READ_TRACE(hdcp, "Rx Status",
(uint8_t *)&hdcp->auth.msg.hdcp2.rxstatus,
sizeof(hdcp->auth.msg.hdcp2.rxstatus));
HDCP_DDC_READ_TRACE(hdcp, "Rx Id List",
hdcp->auth.msg.hdcp2.rx_id_list,
hdcp->auth.msg.hdcp2.rx_id_list_size);
HDCP_DDC_WRITE_TRACE(hdcp, "Rx Id List Ack",
hdcp->auth.msg.hdcp2.repeater_auth_ack,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack));
HDCP_DDC_WRITE_TRACE(hdcp, "Content Stream Management",
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage,
hdcp->auth.msg.hdcp2.stream_manage_size);
HDCP_DDC_READ_TRACE(hdcp, "Stream Ready",
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready));
HDCP_DDC_WRITE_TRACE(hdcp, "Content Stream Type",
hdcp->auth.msg.hdcp2.content_stream_type_dp,
sizeof(hdcp->auth.msg.hdcp2.content_stream_type_dp));
}
}
#define CASE_FORMAT(entry) case entry: return #entry;
char *mod_hdcp_status_to_str(int32_t status)
{
switch (status) {
MOD_HDCP_STATUS_LIST(CASE_FORMAT)
default:
return "MOD_HDCP_STATUS_UNKNOWN";
}
}
char *mod_hdcp_state_id_to_str(int32_t id)
{
switch (id) {
case HDCP_UNINITIALIZED:
return "HDCP_UNINITIALIZED";
case HDCP_INITIALIZED:
return "HDCP_INITIALIZED";
case HDCP_CP_NOT_DESIRED:
return "HDCP_CP_NOT_DESIRED";
case H1_A0_WAIT_FOR_ACTIVE_RX:
return "H1_A0_WAIT_FOR_ACTIVE_RX";
case H1_A1_EXCHANGE_KSVS:
return "H1_A1_EXCHANGE_KSVS";
case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
return "H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER";
case H1_A45_AUTHENTICATED:
return "H1_A45_AUTHENTICATED";
case H1_A8_WAIT_FOR_READY:
return "H1_A8_WAIT_FOR_READY";
case H1_A9_READ_KSV_LIST:
return "H1_A9_READ_KSV_LIST";
case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
return "D1_A0_DETERMINE_RX_HDCP_CAPABLE";
case D1_A1_EXCHANGE_KSVS:
return "D1_A1_EXCHANGE_KSVS";
case D1_A23_WAIT_FOR_R0_PRIME:
return "D1_A23_WAIT_FOR_R0_PRIME";
case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
return "D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER";
case D1_A4_AUTHENTICATED:
return "D1_A4_AUTHENTICATED";
case D1_A6_WAIT_FOR_READY:
return "D1_A6_WAIT_FOR_READY";
case D1_A7_READ_KSV_LIST:
return "D1_A7_READ_KSV_LIST";
case H2_A0_KNOWN_HDCP2_CAPABLE_RX:
return "H2_A0_KNOWN_HDCP2_CAPABLE_RX";
case H2_A1_SEND_AKE_INIT:
return "H2_A1_SEND_AKE_INIT";
case H2_A1_VALIDATE_AKE_CERT:
return "H2_A1_VALIDATE_AKE_CERT";
case H2_A1_SEND_NO_STORED_KM:
return "H2_A1_SEND_NO_STORED_KM";
case H2_A1_READ_H_PRIME:
return "H2_A1_READ_H_PRIME";
case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
return "H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME";
case H2_A1_SEND_STORED_KM:
return "H2_A1_SEND_STORED_KM";
case H2_A1_VALIDATE_H_PRIME:
return "H2_A1_VALIDATE_H_PRIME";
case H2_A2_LOCALITY_CHECK:
return "H2_A2_LOCALITY_CHECK";
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
return "H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER";
case H2_ENABLE_ENCRYPTION:
return "H2_ENABLE_ENCRYPTION";
case H2_A5_AUTHENTICATED:
return "H2_A5_AUTHENTICATED";
case H2_A6_WAIT_FOR_RX_ID_LIST:
return "H2_A6_WAIT_FOR_RX_ID_LIST";
case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
return "H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK";
case H2_A9_SEND_STREAM_MANAGEMENT:
return "H2_A9_SEND_STREAM_MANAGEMENT";
case H2_A9_VALIDATE_STREAM_READY:
return "H2_A9_VALIDATE_STREAM_READY";
case D2_A0_DETERMINE_RX_HDCP_CAPABLE:
return "D2_A0_DETERMINE_RX_HDCP_CAPABLE";
case D2_A1_SEND_AKE_INIT:
return "D2_A1_SEND_AKE_INIT";
case D2_A1_VALIDATE_AKE_CERT:
return "D2_A1_VALIDATE_AKE_CERT";
case D2_A1_SEND_NO_STORED_KM:
return "D2_A1_SEND_NO_STORED_KM";
case D2_A1_READ_H_PRIME:
return "D2_A1_READ_H_PRIME";
case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
return "D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME";
case D2_A1_SEND_STORED_KM:
return "D2_A1_SEND_STORED_KM";
case D2_A1_VALIDATE_H_PRIME:
return "D2_A1_VALIDATE_H_PRIME";
case D2_A2_LOCALITY_CHECK:
return "D2_A2_LOCALITY_CHECK";
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
return "D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER";
case D2_SEND_CONTENT_STREAM_TYPE:
return "D2_SEND_CONTENT_STREAM_TYPE";
case D2_ENABLE_ENCRYPTION:
return "D2_ENABLE_ENCRYPTION";
case D2_A5_AUTHENTICATED:
return "D2_A5_AUTHENTICATED";
case D2_A6_WAIT_FOR_RX_ID_LIST:
return "D2_A6_WAIT_FOR_RX_ID_LIST";
case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
return "D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK";
case D2_A9_SEND_STREAM_MANAGEMENT:
return "D2_A9_SEND_STREAM_MANAGEMENT";
case D2_A9_VALIDATE_STREAM_READY:
return "D2_A9_VALIDATE_STREAM_READY";
default:
return "UNKNOWN_STATE_ID";
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef MOD_HDCP_LOG_H_
#define MOD_HDCP_LOG_H_
#define HDCP_LOG_ERR(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_VER(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_FSM(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_TOP(hdcp, ...) pr_debug("[HDCP_TOP]:"__VA_ARGS__)
#define HDCP_LOG_DDC(hdcp, ...) pr_debug("[HDCP_DDC]:"__VA_ARGS__)
#define HDCP_LOG_TRA(hdcp) do {} while (0)
/* default logs */
#define HDCP_ERROR_TRACE(hdcp, status) \
HDCP_LOG_ERR(hdcp, \
"[Link %d] WARNING %s IN STATE %s STAY COUNT %d", \
hdcp->config.index, \
mod_hdcp_status_to_str(status), \
mod_hdcp_state_id_to_str(hdcp->state.id), \
hdcp->state.stay_count)
#define HDCP_HDCP1_ENABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 1.4 enabled on display %d", \
hdcp->config.index, displayIndex)
#define HDCP_HDCP2_ENABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 2.2 enabled on display %d", \
hdcp->config.index, displayIndex)
#define HDCP_HDCP1_DISABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 1.4 disabled on display %d", \
hdcp->config.index, displayIndex)
#define HDCP_HDCP2_DISABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 2.2 disabled on display %d", \
hdcp->config.index, displayIndex)
/* state machine logs */
#define HDCP_REMOVE_DISPLAY_TRACE(hdcp, displayIndex) \
HDCP_LOG_FSM(hdcp, \
"[Link %d] HDCP_REMOVE_DISPLAY index %d", \
hdcp->config.index, displayIndex)
#define HDCP_INPUT_PASS_TRACE(hdcp, str) \
HDCP_LOG_FSM(hdcp, \
"[Link %d]\tPASS %s", \
hdcp->config.index, str)
#define HDCP_INPUT_FAIL_TRACE(hdcp, str) \
HDCP_LOG_FSM(hdcp, \
"[Link %d]\tFAIL %s", \
hdcp->config.index, str)
#define HDCP_NEXT_STATE_TRACE(hdcp, id, output) do { \
if (output->watchdog_timer_needed) \
HDCP_LOG_FSM(hdcp, \
"[Link %d] > %s with %d ms watchdog", \
hdcp->config.index, \
mod_hdcp_state_id_to_str(id), output->watchdog_timer_delay); \
else \
HDCP_LOG_FSM(hdcp, \
"[Link %d] > %s", hdcp->config.index, \
mod_hdcp_state_id_to_str(id)); \
} while (0)
#define HDCP_TIMEOUT_TRACE(hdcp) \
HDCP_LOG_FSM(hdcp, "[Link %d] --> TIMEOUT", hdcp->config.index)
#define HDCP_CPIRQ_TRACE(hdcp) \
HDCP_LOG_FSM(hdcp, "[Link %d] --> CPIRQ", hdcp->config.index)
#define HDCP_EVENT_TRACE(hdcp, event) \
do { \
if (event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) \
HDCP_TIMEOUT_TRACE(hdcp); \
else if (event == MOD_HDCP_EVENT_CPIRQ) \
HDCP_CPIRQ_TRACE(hdcp); \
} while (0)
/* TODO: find some way to tell if logging is off to save time */
#define HDCP_DDC_READ_TRACE(hdcp, msg_name, msg, msg_size) do { \
mod_hdcp_dump_binary_message(msg, msg_size, hdcp->buf, \
sizeof(hdcp->buf)); \
HDCP_LOG_DDC(hdcp, "[Link %d] Read %s%s", hdcp->config.index, \
msg_name, hdcp->buf); \
} while (0)
#define HDCP_DDC_WRITE_TRACE(hdcp, msg_name, msg, msg_size) do { \
mod_hdcp_dump_binary_message(msg, msg_size, hdcp->buf, \
sizeof(hdcp->buf)); \
HDCP_LOG_DDC(hdcp, "[Link %d] Write %s%s", \
hdcp->config.index, msg_name,\
hdcp->buf); \
} while (0)
#define HDCP_TOP_ADD_DISPLAY_TRACE(hdcp, i) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tadd display %d", \
hdcp->config.index, i)
#define HDCP_TOP_REMOVE_DISPLAY_TRACE(hdcp, i) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tremove display %d", \
hdcp->config.index, i)
#define HDCP_TOP_HDCP1_DESTROY_SESSION_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tdestroy hdcp1 session", \
hdcp->config.index)
#define HDCP_TOP_HDCP2_DESTROY_SESSION_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tdestroy hdcp2 session", \
hdcp->config.index)
#define HDCP_TOP_RESET_AUTH_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\treset authentication", hdcp->config.index)
#define HDCP_TOP_RESET_CONN_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\treset connection", hdcp->config.index)
#define HDCP_TOP_INTERFACE_TRACE(hdcp) do { \
HDCP_LOG_TOP(hdcp, "\n"); \
HDCP_LOG_TOP(hdcp, "[Link %d] %s", hdcp->config.index, __func__); \
} while (0)
#define HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, i) do { \
HDCP_LOG_TOP(hdcp, "\n"); \
HDCP_LOG_TOP(hdcp, "[Link %d] %s display %d", hdcp->config.index, __func__, i); \
} while (0)
#define HDCP_AUTH_COMPLETE_TRACE(hdcp) do { \
mod_hdcp_log_ddc_trace(hdcp); \
HDCP_LOG_TRA(hdcp); \
} while (0)
#endif // MOD_HDCP_LOG_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,534 @@
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef MODULES_HDCP_HDCP_PSP_H_
#define MODULES_HDCP_HDCP_PSP_H_
/*
* NOTE: These parameters are a one-to-one copy of the
* parameters required by PSP
*/
enum bgd_security_hdcp_encryption_level {
HDCP_ENCRYPTION_LEVEL__INVALID = 0,
HDCP_ENCRYPTION_LEVEL__OFF,
HDCP_ENCRYPTION_LEVEL__ON
};
enum bgd_security_hdcp2_content_type {
HDCP2_CONTENT_TYPE__INVALID = 0,
HDCP2_CONTENT_TYPE__TYPE0,
HDCP2_CONTENT_TYPE__TYPE1
};
enum ta_dtm_command {
TA_DTM_COMMAND__UNUSED_1 = 1,
TA_DTM_COMMAND__TOPOLOGY_UPDATE_V2,
TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE,
TA_DTM_COMMAND__TOPOLOGY_UPDATE_V3
};
/* DTM related enumerations */
/**********************************************************/
enum ta_dtm_status {
TA_DTM_STATUS__SUCCESS = 0x00,
TA_DTM_STATUS__GENERIC_FAILURE = 0x01,
TA_DTM_STATUS__INVALID_PARAMETER = 0x02,
TA_DTM_STATUS__NULL_POINTER = 0x3
};
/* input/output structures for DTM commands */
/**********************************************************/
/**
* Input structures
*/
enum ta_dtm_hdcp_version_max_supported {
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__NONE = 0,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__1_x = 10,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_0 = 20,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_1 = 21,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_2 = 22,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_3 = 23
};
struct ta_dtm_topology_update_input_v2 {
/* display handle is unique across the driver and is used to identify a display */
/* for all security interfaces which reference displays such as HDCP */
uint32_t display_handle;
uint32_t is_active;
uint32_t is_miracast;
uint32_t controller;
uint32_t ddc_line;
uint32_t dig_be;
uint32_t dig_fe;
uint32_t dp_mst_vcid;
uint32_t is_assr;
/*uint32_t is_frl;*/ /*todo*/
uint32_t max_hdcp_supported_version;
};
/* For security reason/HW may change value, these encoder type enum values are not HW register values */
/* Security code will check real HW register values and these SW enum values */
enum ta_dtm_encoder_type {
TA_DTM_ENCODER_TYPE__INVALID = 0,
TA_DTM_ENCODER_TYPE__FRL = 0x20,
TA_DTM_ENCODER_TYPE__DIG = 0x10
};
/* @enum ta_dtm_dio_output_type
* This enum defines software value for dio_output_type
*/
typedef enum {
TA_DTM_DIO_OUTPUT_TYPE__INVALID,
TA_DTM_DIO_OUTPUT_TYPE__DIRECT,
TA_DTM_DIO_OUTPUT_TYPE__DPIA
} ta_dtm_dio_output_type;
struct ta_dtm_topology_update_input_v3 {
/* display handle is unique across the driver and is used to identify a display */
/* for all security interfaces which reference displays such as HDCP */
/* link_hdcp_cap means link is HDCP-capable for audio HDCP capable property(informational), not for other logic(e.g. Crossbar) */
uint32_t display_handle;
uint32_t is_active;
uint32_t is_miracast;
uint32_t controller;
uint32_t ddc_line;
uint32_t link_enc;
uint32_t stream_enc;
uint32_t dp_mst_vcid;
uint32_t is_assr;
uint32_t max_hdcp_supported_version;
enum ta_dtm_encoder_type encoder_type;
uint32_t phy_id;
uint32_t link_hdcp_cap;
ta_dtm_dio_output_type dio_output_type;
uint32_t dio_output_id;
};
struct ta_dtm_topology_assr_enable {
uint32_t display_topology_dig_be_index;
};
/**
* Output structures
*/
/* No output structures yet */
union ta_dtm_cmd_input {
struct ta_dtm_topology_update_input_v2 topology_update_v2;
struct ta_dtm_topology_assr_enable topology_assr_enable;
struct ta_dtm_topology_update_input_v3 topology_update_v3;
};
union ta_dtm_cmd_output {
uint32_t reserved;
};
struct ta_dtm_shared_memory {
uint32_t cmd_id;
uint32_t resp_id;
enum ta_dtm_status dtm_status;
uint32_t reserved;
union ta_dtm_cmd_input dtm_in_message;
union ta_dtm_cmd_output dtm_out_message;
};
int psp_cmd_submit_buf(struct psp_context *psp, struct amdgpu_firmware_info *ucode, struct psp_gfx_cmd_resp *cmd,
uint64_t fence_mc_addr);
enum { PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE = 5120 };
enum ta_hdcp_command {
TA_HDCP_COMMAND__INITIALIZE,
TA_HDCP_COMMAND__HDCP1_CREATE_SESSION,
TA_HDCP_COMMAND__HDCP1_DESTROY_SESSION,
TA_HDCP_COMMAND__HDCP1_FIRST_PART_AUTHENTICATION,
TA_HDCP_COMMAND__HDCP1_SECOND_PART_AUTHENTICATION,
TA_HDCP_COMMAND__HDCP1_ENABLE_ENCRYPTION,
TA_HDCP_COMMAND__HDCP1_ENABLE_DP_STREAM_ENCRYPTION,
TA_HDCP_COMMAND__HDCP1_GET_ENCRYPTION_STATUS,
TA_HDCP_COMMAND__UNUSED_1,
TA_HDCP_COMMAND__HDCP2_DESTROY_SESSION,
TA_HDCP_COMMAND__UNUSED_2,
TA_HDCP_COMMAND__HDCP2_SET_ENCRYPTION,
TA_HDCP_COMMAND__HDCP2_GET_ENCRYPTION_STATUS,
TA_HDCP_COMMAND__UNUSED_3,
TA_HDCP_COMMAND__HDCP2_CREATE_SESSION_V2,
TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2,
TA_HDCP_COMMAND__HDCP2_ENABLE_DP_STREAM_ENCRYPTION,
TA_HDCP_COMMAND__HDCP_DESTROY_ALL_SESSIONS,
TA_HDCP_COMMAND__HDCP_SET_SRM,
TA_HDCP_COMMAND__HDCP_GET_SRM
};
enum ta_hdcp2_msg_id {
TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE = 1,
TA_HDCP_HDCP2_MSG_ID__AKE_INIT = 2,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_CERT = 3,
TA_HDCP_HDCP2_MSG_ID__AKE_NO_STORED_KM = 4,
TA_HDCP_HDCP2_MSG_ID__AKE_STORED_KM = 5,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_RRX = 6,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_H_PRIME = 7,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_PAIRING_INFO = 8,
TA_HDCP_HDCP2_MSG_ID__LC_INIT = 9,
TA_HDCP_HDCP2_MSG_ID__LC_SEND_L_PRIME = 10,
TA_HDCP_HDCP2_MSG_ID__SKE_SEND_EKS = 11,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_SEND_RECEIVERID_LIST = 12,
TA_HDCP_HDCP2_MSG_ID__RTT_READY = 13,
TA_HDCP_HDCP2_MSG_ID__RTT_CHALLENGE = 14,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_SEND_ACK = 15,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_STREAM_MANAGE = 16,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_STREAM_READY = 17,
TA_HDCP_HDCP2_MSG_ID__RECEIVER_AUTH_STATUS = 18,
TA_HDCP_HDCP2_MSG_ID__AKE_TRANSMITTER_INFO = 19,
TA_HDCP_HDCP2_MSG_ID__AKE_RECEIVER_INFO = 20,
TA_HDCP_HDCP2_MSG_ID__SIGNAL_CONTENT_STREAM_TYPE_DP = 129
};
enum ta_hdcp2_hdcp2_msg_id_max_size {
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__NULL_MESSAGE = 0,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_INIT = 12,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_CERT = 534,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_NO_STORED_KM = 129,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_STORED_KM = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_RRX = 9,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_H_PRIME = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_PAIRING_INFO = 17,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__LC_INIT = 9,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__LC_SEND_L_PRIME = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__SKE_SEND_EKS = 25,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_SEND_RECEIVERID_LIST = 181,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__RTT_READY = 1,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__RTT_CHALLENGE = 17,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_SEND_RACK = 17,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_STREAM_MANAGE = 13,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_STREAM_READY = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__RECEIVER_AUTH_STATUS = 4,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_TRANSMITTER_INFO = 6,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_RECEIVER_INFO = 6,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__SIGNAL_CONTENT_STREAM_TYPE_DP = 1
};
/* HDCP related enumerations */
/**********************************************************/
#define TA_HDCP__INVALID_SESSION 0xFFFF
#define TA_HDCP__HDCP1_AN_SIZE 8
#define TA_HDCP__HDCP1_KSV_SIZE 5
#define TA_HDCP__HDCP1_KSV_LIST_MAX_ENTRIES 127
#define TA_HDCP__HDCP1_V_PRIME_SIZE 20
#define TA_HDCP__HDCP2_TX_BUF_MAX_SIZE \
(TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_NO_STORED_KM + TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_STORED_KM + 6)
// 64 bits boundaries
#define TA_HDCP__HDCP2_RX_BUF_MAX_SIZE \
(TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_CERT + TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_RECEIVER_INFO + 4)
enum ta_hdcp_status {
TA_HDCP_STATUS__SUCCESS = 0x00,
TA_HDCP_STATUS__GENERIC_FAILURE = 0x01,
TA_HDCP_STATUS__NULL_POINTER = 0x02,
TA_HDCP_STATUS__FAILED_ALLOCATING_SESSION = 0x03,
TA_HDCP_STATUS__FAILED_SETUP_TX = 0x04,
TA_HDCP_STATUS__INVALID_PARAMETER = 0x05,
TA_HDCP_STATUS__VHX_ERROR = 0x06,
TA_HDCP_STATUS__SESSION_NOT_CLOSED_PROPERLY = 0x07,
TA_HDCP_STATUS__SRM_FAILURE = 0x08,
TA_HDCP_STATUS__MST_AUTHENTICATED_ALREADY_STARTED = 0x09,
TA_HDCP_STATUS__AKE_SEND_CERT_FAILURE = 0x0A,
TA_HDCP_STATUS__AKE_NO_STORED_KM_FAILURE = 0x0B,
TA_HDCP_STATUS__AKE_SEND_HPRIME_FAILURE = 0x0C,
TA_HDCP_STATUS__LC_SEND_LPRIME_FAILURE = 0x0D,
TA_HDCP_STATUS__SKE_SEND_EKS_FAILURE = 0x0E,
TA_HDCP_STATUS__REPAUTH_SEND_RXIDLIST_FAILURE = 0x0F,
TA_HDCP_STATUS__REPAUTH_STREAM_READY_FAILURE = 0x10,
TA_HDCP_STATUS__ASD_GENERIC_FAILURE = 0x11,
TA_HDCP_STATUS__UNWRAP_SECRET_FAILURE = 0x12,
TA_HDCP_STATUS__ENABLE_ENCR_FAILURE = 0x13,
TA_HDCP_STATUS__DISABLE_ENCR_FAILURE = 0x14,
TA_HDCP_STATUS__NOT_ENOUGH_MEMORY_FAILURE = 0x15,
TA_HDCP_STATUS__UNKNOWN_MESSAGE = 0x16,
TA_HDCP_STATUS__TOO_MANY_STREAM = 0x17
};
enum ta_hdcp_authentication_status {
TA_HDCP_AUTHENTICATION_STATUS__NOT_STARTED = 0x00,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_FIRST_PART_FAILED = 0x01,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_FIRST_PART_COMPLETE = 0x02,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_SECOND_PART_FAILED = 0x03,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_AUTHENTICATED = 0x04,
TA_HDCP_AUTHENTICATION_STATUS__HDCP22_AUTHENTICATION_PENDING = 0x06,
TA_HDCP_AUTHENTICATION_STATUS__HDCP22_AUTHENTICATION_FAILED = 0x07,
TA_HDCP_AUTHENTICATION_STATUS__HDCP22_AUTHENTICATED = 0x08,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_KSV_VALIDATION_FAILED = 0x09,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_KSV_REVOKED = 0x0A
};
enum ta_hdcp2_msg_authentication_status {
TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS = 0,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__KM_NOT_AVAILABLE,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__UNUSED,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID = 100, // everything above does not fail the request
TA_HDCP2_MSG_AUTHENTICATION_STATUS__NOT_ENOUGH_MEMORY,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__NOT_EXPECTED_MSG,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__SIGNATURE_CERTIFICAT_ERROR,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INCORRECT_HDCP_VERSION,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__UNKNOWN_MESSAGE,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_HMAC,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_TOPOLOGY,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_SEQ_NUM,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_SIZE,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_LENGTH,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__REAUTH_REQUEST,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__RECEIVERID_REVOKED
};
enum ta_hdcp_content_type {
TA_HDCP2_CONTENT_TYPE__TYPE0 = 1,
TA_HDCP2_CONTENT_TYPE__TYPE1,
};
enum ta_hdcp_content_type_negotiation_type {
TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__FORCE_TYPE0 = 1,
TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__FORCE_TYPE1,
TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__MAX_SUPPORTED
};
enum ta_hdcp2_version {
TA_HDCP2_VERSION_UNKNOWN = 0,
TA_HDCP2_VERSION_2_0 = 20,
TA_HDCP2_VERSION_2_1 = 21,
TA_HDCP2_VERSION_2_2 = 22,
TA_HDCP2_VERSION_2_3 = 23,
};
/* input/output structures for HDCP commands */
/**********************************************************/
struct ta_hdcp_cmd_hdcp1_create_session_input {
uint8_t display_handle;
};
struct ta_hdcp_cmd_hdcp1_create_session_output {
uint32_t session_handle;
uint8_t an_primary[TA_HDCP__HDCP1_AN_SIZE];
uint8_t aksv_primary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t ainfo_primary;
uint8_t an_secondary[TA_HDCP__HDCP1_AN_SIZE];
uint8_t aksv_secondary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t ainfo_secondary;
};
struct ta_hdcp_cmd_hdcp1_destroy_session_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp1_first_part_authentication_input {
uint32_t session_handle;
uint8_t bksv_primary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t bksv_secondary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t bcaps;
uint16_t r0_prime_primary;
uint16_t r0_prime_secondary;
};
struct ta_hdcp_cmd_hdcp1_first_part_authentication_output {
enum ta_hdcp_authentication_status authentication_status;
};
struct ta_hdcp_cmd_hdcp1_second_part_authentication_input {
uint32_t session_handle;
uint16_t bstatus_binfo;
uint8_t ksv_list[TA_HDCP__HDCP1_KSV_LIST_MAX_ENTRIES][TA_HDCP__HDCP1_KSV_SIZE];
uint32_t ksv_list_size;
uint8_t pj_prime;
uint8_t v_prime[TA_HDCP__HDCP1_V_PRIME_SIZE];
};
struct ta_hdcp_cmd_hdcp1_second_part_authentication_output {
enum ta_hdcp_authentication_status authentication_status;
};
struct ta_hdcp_cmd_hdcp1_enable_encryption_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp1_enable_dp_stream_encryption_input {
uint32_t session_handle;
uint32_t display_handle;
};
struct ta_hdcp_cmd_hdcp1_get_encryption_status_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp1_get_encryption_status_output {
uint32_t protection_level;
};
struct ta_hdcp_cmd_hdcp2_create_session_input_v2 {
uint32_t display_handle;
enum ta_hdcp_content_type_negotiation_type negotiate_content_type;
};
struct ta_hdcp_cmd_hdcp2_create_session_output_v2 {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_destroy_session_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 {
enum ta_hdcp2_msg_id msg_id;
uint32_t msg_size;
};
struct ta_hdcp_cmd_hdcp2_process_authentication_message_input_v2 {
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg1_desc;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg2_desc;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg3_desc;
uint8_t receiver_message[TA_HDCP__HDCP2_RX_BUF_MAX_SIZE];
};
struct ta_hdcp_cmd_hdcp2_process_authentication_message_output_v2 {
uint32_t hdcp_version;
uint32_t is_km_stored;
uint32_t is_locality_precompute_support;
uint32_t is_repeater;
enum ta_hdcp2_msg_authentication_status msg1_status;
enum ta_hdcp2_msg_authentication_status msg2_status;
enum ta_hdcp2_msg_authentication_status msg3_status;
};
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_input_v2 {
enum ta_hdcp2_msg_id msg1_id;
enum ta_hdcp2_msg_id msg2_id;
};
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_output_v2 {
enum ta_hdcp2_msg_authentication_status msg1_status;
enum ta_hdcp2_msg_authentication_status msg2_status;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg1_desc;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg2_desc;
uint8_t transmitter_message[TA_HDCP__HDCP2_TX_BUF_MAX_SIZE];
};
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 {
uint32_t session_handle;
struct ta_hdcp_cmd_hdcp2_process_authentication_message_input_v2 process;
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_input_v2 prepare;
};
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 {
uint32_t authentication_status;
struct ta_hdcp_cmd_hdcp2_process_authentication_message_output_v2 process;
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_output_v2 prepare;
};
struct ta_hdcp_cmd_hdcp2_set_encryption_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_get_encryption_status_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_get_encryption_status_output {
enum ta_hdcp_content_type hdcp2_type;
uint32_t protection_level;
};
struct ta_hdcp_cmd_hdcp2_enable_dp_stream_encryption_input {
uint32_t session_handle;
uint32_t display_handle;
};
struct ta_hdcp_cmd_set_srm_input {
uint32_t srm_buf_size;
uint8_t srm_buf[PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE];
};
struct ta_hdcp_cmd_set_srm_output {
uint8_t valid_signature;
uint32_t srm_version;
};
struct ta_hdcp_cmd_get_srm_output {
uint32_t srm_version;
uint32_t srm_buf_size;
uint8_t srm_buf[PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE];
};
/**********************************************************/
/* Common input structure for HDCP callbacks */
union ta_hdcp_cmd_input {
struct ta_hdcp_cmd_hdcp1_create_session_input hdcp1_create_session;
struct ta_hdcp_cmd_hdcp1_destroy_session_input hdcp1_destroy_session;
struct ta_hdcp_cmd_hdcp1_first_part_authentication_input hdcp1_first_part_authentication;
struct ta_hdcp_cmd_hdcp1_second_part_authentication_input hdcp1_second_part_authentication;
struct ta_hdcp_cmd_hdcp1_enable_encryption_input hdcp1_enable_encryption;
struct ta_hdcp_cmd_hdcp1_enable_dp_stream_encryption_input hdcp1_enable_dp_stream_encryption;
struct ta_hdcp_cmd_hdcp1_get_encryption_status_input hdcp1_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_destroy_session_input hdcp2_destroy_session;
struct ta_hdcp_cmd_hdcp2_set_encryption_input hdcp2_set_encryption;
struct ta_hdcp_cmd_hdcp2_get_encryption_status_input hdcp2_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_create_session_input_v2 hdcp2_create_session_v2;
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2
hdcp2_prepare_process_authentication_message_v2;
struct ta_hdcp_cmd_hdcp2_enable_dp_stream_encryption_input hdcp2_enable_dp_stream_encryption;
struct ta_hdcp_cmd_set_srm_input hdcp_set_srm;
};
/* Common output structure for HDCP callbacks */
union ta_hdcp_cmd_output {
struct ta_hdcp_cmd_hdcp1_create_session_output hdcp1_create_session;
struct ta_hdcp_cmd_hdcp1_first_part_authentication_output hdcp1_first_part_authentication;
struct ta_hdcp_cmd_hdcp1_second_part_authentication_output hdcp1_second_part_authentication;
struct ta_hdcp_cmd_hdcp1_get_encryption_status_output hdcp1_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_get_encryption_status_output hdcp2_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_create_session_output_v2 hdcp2_create_session_v2;
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2
hdcp2_prepare_process_authentication_message_v2;
struct ta_hdcp_cmd_set_srm_output hdcp_set_srm;
struct ta_hdcp_cmd_get_srm_output hdcp_get_srm;
};
/**********************************************************/
struct ta_hdcp_shared_memory {
uint32_t cmd_id;
enum ta_hdcp_status hdcp_status;
uint32_t reserved;
union ta_hdcp_cmd_input in_msg;
union ta_hdcp_cmd_output out_msg;
};
enum psp_status {
PSP_STATUS__SUCCESS = 0,
PSP_STATUS__ERROR_INVALID_PARAMS,
PSP_STATUS__ERROR_GENERIC,
PSP_STATUS__ERROR_OUT_OF_MEMORY,
PSP_STATUS__ERROR_UNSUPPORTED_FEATURE
};
#endif /* MODULES_HDCP_HDCP_PSP_H_ */