/* * drivers/soc/samsung/exynos-hdcp/exynos-hdcp.c * * Copyright (c) 2016 Samsung Electronics Co., Ltd. * http://www.samsung.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "exynos-hdcp2-iia-auth.h" #include "../exynos-hdcp2-teeif.h" #include "exynos-hdcp2-iia-selftest.h" #include "../exynos-hdcp2.h" #include "../exynos-hdcp2-encrypt.h" #include "../exynos-hdcp2-log.h" #include "../exynos-hdcp2-session.h" #define EXYNOS_HDCP_DEV_NAME "hdcp2" enum hdcp_result hdcp_link_ioc_authenticate(void); static int state_init_flag; enum hdcp_result hdcp_unwrap_key(char *wkey) { int rval = TX_AUTH_SUCCESS; rval = teei_wrapped_key(wkey, UNWRAP, HDCP_STATIC_KEY); if (rval) { hdcp_err("Wrap(%d) key failed (0x%08x)\n", UNWRAP, rval); return HDCP_ERROR_UNWRAP_FAIL; } return 0; } enum hdcp_result hdcp_link_authenticate(struct hdcp_msg_info *msg_info) { struct hdcp_session_node *ss_node; struct hdcp_link_node *lk_node; struct hdcp_link_data *lk_data; int ret = HDCP_SUCCESS; int rval = TX_AUTH_SUCCESS; int ake_retry = 0; int lc_retry = 0; /* find Session node which contains the Link */ ss_node = hdcp_session_list_find(msg_info->ss_handle); if (!ss_node) return HDCP_ERROR_INVALID_INPUT; lk_node = hdcp_link_list_find(msg_info->lk_id, &ss_node->ss_data->ln); if (!lk_node) return HDCP_ERROR_INVALID_INPUT; lk_data = lk_node->lk_data; if (!lk_data) return HDCP_ERROR_INVALID_INPUT; /** * if Upstream Content Control Function call this API, * it changes state to ST_A0_DETERMINE_RX_HDCP_CAP automatically. * HDCP library do not check CP desire. */ if (state_init_flag == 0){ UPDATE_LINK_STATE(lk_data, LINK_ST_A0_DETERMINE_RX_HDCP_CAP); } if (lk_data->state == LINK_ST_A0_DETERMINE_RX_HDCP_CAP){ if (determine_rx_hdcp_cap(lk_data) < 0) { ret = HDCP_ERROR_RX_NOT_HDCP_CAPABLE; UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); } else UPDATE_LINK_STATE(lk_data, LINK_ST_A1_EXCHANGE_MASTER_KEY); } switch (lk_data->state) { case LINK_ST_H1_TX_LOW_VALUE_CONTENT: break; case LINK_ST_A1_EXCHANGE_MASTER_KEY: rval = exchange_master_key(lk_data, msg_info); if (rval == TX_AUTH_SUCCESS) { if (msg_info->next_step == DONE) { ake_retry = 0; UPDATE_LINK_STATE(lk_data, LINK_ST_A2_LOCALITY_CHECK); msg_info->next_step = SEND_MSG; state_init_flag = 1; } } else { ret = HDCP_ERROR_EXCHANGE_KM; UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); } break; case LINK_ST_A2_LOCALITY_CHECK: rval = locality_check(lk_data, msg_info); if (rval == TX_AUTH_SUCCESS) { if (msg_info->next_step == DONE) { lc_retry = 0; UPDATE_LINK_STATE(lk_data, LINK_ST_A3_EXCHANGE_SESSION_KEY); msg_info->next_step = SEND_MSG; } } else { UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); } break; case LINK_ST_A3_EXCHANGE_SESSION_KEY: if (exchange_hdcp_session_key(lk_data, msg_info) < 0) { ret = HDCP_ERROR_EXCHANGE_KS; UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); } else{ msg_info->next_step = WAIT_STATE; UPDATE_LINK_STATE(lk_data, LINK_ST_A4_TEST_REPEATER); } break; case LINK_ST_A4_TEST_REPEATER: if (evaluate_repeater(lk_data) == TRUE){ /* HACK: when we supports repeater, it should be removed */ UPDATE_LINK_STATE(lk_data, LINK_ST_A6_WAIT_RECEIVER_ID_LIST); msg_info->next_step = RP_RECIEVE_MSG; } else{ /* if it is not a repeater, complete authentication */ UPDATE_LINK_STATE(lk_data, LINK_ST_A5_AUTHENTICATED); } break; case LINK_ST_A5_AUTHENTICATED: msg_info->next_step = AUTH_FINISHED; state_init_flag = 0; return HDCP_SUCCESS; case LINK_ST_A6_WAIT_RECEIVER_ID_LIST: ret = wait_for_receiver_id_list(lk_data, msg_info); if (ret < 0) { ret = HDCP_ERROR_WAIT_RECEIVER_ID_LIST; UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); } else { UPDATE_LINK_STATE(lk_data, LINK_ST_A7_VERIFY_RECEIVER_ID_LIST); msg_info->next_step = WAIT_STATE; } break; case LINK_ST_A7_VERIFY_RECEIVER_ID_LIST: UPDATE_LINK_STATE(lk_data, LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK); msg_info->next_step = RP_SEND_MSG; break; case LINK_ST_A8_SEND_RECEIVER_ID_LIST_ACK: rval = send_receiver_id_list_ack(lk_data, msg_info); if (rval == TX_AUTH_SUCCESS) { if (msg_info->next_step == DONE) { UPDATE_LINK_STATE(lk_data, LINK_ST_A5_AUTHENTICATED); state_init_flag = 0; msg_info->next_step = AUTH_FINISHED; } } else { UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); } break; case LINK_ST_A9_CONTENT_STREAM_MGT: /* do not support yet */ ret = HDCP_ERROR_DO_NOT_SUPPORT_YET; break; default: ret = HDCP_ERROR_INVALID_STATE; UPDATE_LINK_STATE(lk_data, LINK_ST_H1_TX_LOW_VALUE_CONTENT); break; } return ret; } enum hdcp_result hdcp_link_stream_manage(struct hdcp_stream_info *stream_info) { struct hdcp_session_node *ss_node; struct hdcp_link_node *lk_node; struct hdcp_link_data *lk_data; struct contents_info stream_ctrl; int rval = TX_AUTH_SUCCESS; int i; /* find Session node which contain the Link */ ss_node = hdcp_session_list_find(stream_info->ss_id); if (!ss_node) return HDCP_ERROR_INVALID_INPUT; lk_node = hdcp_link_list_find(stream_info->lk_id, &ss_node->ss_data->ln); if (!lk_node) return HDCP_ERROR_INVALID_INPUT; lk_data = lk_node->lk_data; if (!lk_data) return HDCP_ERROR_INVALID_INPUT; if (lk_data->state < LINK_ST_A4_TEST_REPEATER) return HDCP_ERROR_INVALID_STATE; stream_ctrl.str_num = stream_info->num; if (stream_info->num > HDCP_TX_REPEATER_MAX_STREAM) return HDCP_ERROR_INVALID_INPUT; for (i = 0; i < stream_info->num; i++) { stream_ctrl.str_info[i].ctr = stream_info->stream_ctr; stream_ctrl.str_info[i].type = stream_info->type; stream_ctrl.str_info[i].pid = stream_info->stream_pid; } rval = manage_content_stream(lk_data, &stream_ctrl, stream_info); if (rval < 0) { hdcp_err("manage_content_stream fail(0x%08x)\n", rval); return HDCP_ERROR_STREAM_MANAGE; } return HDCP_SUCCESS; } enum hdcp_result hdcp_wrap_key(struct hdcp_wrapped_key *key_info) { int rval = TX_AUTH_SUCCESS; char key_str[HDCP_WRAP_MAX_SIZE] = {0}; if (key_info->key_len <= HDCP_WRAP_KEY) memcpy(key_str, key_info->key, HDCP_WRAP_KEY); else return HDCP_ERROR_WRONG_SIZE; rval = teei_wrapped_key(key_str, key_info->wrapped, key_info->key_len); if (rval) { hdcp_err("Wrap(%d) key failed (0x%08x)\n",key_info->wrapped, rval); return HDCP_ERROR_WRAP_FAIL; } if (key_info->wrapped == WRAP) memcpy(key_info->enc_key, key_str, HDCP_WRAP_KEY + HDCP_WRAP_AUTH_TAG); return 0; } MODULE_LICENSE("GPL");