blob: c31478e53f24371c9be0cf55b446d4bbf01d22a2 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Samsung DisplayPort driver.
*
* 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 <drm/drm_dp_helper.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include "auth22-internal.h"
#include "dpcd.h"
#include "teeif.h"
#include "hdcp-log.h"
#define DEV_COUNT_SHIFT (4)
#define DEV_COUNT_MASK (0x1F)
static int cal_rcvid_list_size(uint8_t *rxinfo)
{
uint8_t dev_count;
uint16_t rxinfo_val;
memcpy((uint8_t *)&rxinfo_val, rxinfo, sizeof(rxinfo_val));
rxinfo_val = htons(rxinfo_val);
dev_count = (rxinfo_val >> DEV_COUNT_SHIFT) & DEV_COUNT_MASK;
return HDCP_RCV_ID_LEN * dev_count;
}
int auth22_wait_for_receiver_id_list(struct hdcp_link_data *lk)
{
int i = 0;
int ret;
uint8_t status = 0;
/* HDCP spec is 5 sec */
while (i < 50) {
/* check abort state firstly,
* if session is abored by Rx, Tx stops Authentication process
*/
if (lk->is_aborted)
return -ECANCELED;
/* received from CP_IRQ */
if (lk->rp_ready) {
/* reset flag */
lk->rp_ready = 0;
return 0;
}
/* check as polling mode */
ret = hdcp_dplink_recv(DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &status,
sizeof(uint8_t));
hdcp_info("RxStatus: %x\n", status);
if (ret == 0 && HDCP_2_2_DP_RXSTATUS_READY(status)) {
/* reset flag */
lk->rp_ready = 0;
return 0;
}
msleep(110);
i++;
}
hdcp_err("receiver ID list timeout(%dms)\n", (110 * i));
return -ETIMEDOUT;
}
int auth22_verify_receiver_id_list(struct hdcp_link_data *lk) {
int ret;
uint8_t rx_info[HDCP_RP_RX_INFO_LEN];
uint8_t seq_num_v[HDCP_RP_SEQ_NUM_V_LEN];
uint8_t v_prime[HDCP_RP_HMAC_V_LEN / 2];
uint8_t rcvid_list[HDCP_RP_RCVID_LIST_LEN];
uint8_t v[HDCP_RP_HMAC_V_LEN / 2];
uint8_t valid;
if (lk->is_aborted)
return -ECANCELED;
ret = hdcp_dplink_recv(DP_HDCP_2_2_REG_RXINFO_OFFSET, rx_info,
sizeof(rx_info));
if (ret) {
hdcp_err("rx_info rcv fail: ret(%d)\n", ret);
return -EIO;
}
ret = hdcp_dplink_recv(DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, seq_num_v,
sizeof(seq_num_v));
if (ret) {
hdcp_err("seq_num_v rcv fail: ret(%d)\n", ret);
return -EIO;
}
ret = hdcp_dplink_recv(DP_HDCP_2_2_REG_VPRIME_OFFSET, v_prime,
sizeof(v_prime));
if (ret) {
hdcp_err("v_prime rcv fail: ret(%d)\n", ret);
return -EIO;
}
ret = hdcp_dplink_recv(DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, rcvid_list,
cal_rcvid_list_size(rx_info));
if (ret) {
hdcp_err("rcvid_list rcv fail: ret(%d)\n", ret);
return -EIO;
}
/* set receiver id list */
ret = teei_set_rcvlist_info(rx_info, seq_num_v, v_prime, rcvid_list,
v, &valid);
if (ret) {
hdcp_err("teei_set_rcvid_list() failed %d\n", ret);
return -EIO;
}
if (valid == 0) {
hdcp_err("vprime verification failed\n");
return -EIO;
}
ret = hdcp_dplink_send(DP_HDCP_2_2_REG_V_OFFSET, v,
HDCP_RP_HMAC_V_LEN / 2);
if (ret) {
hdcp_err("V send fail: ret(%d)\n", ret);
return -EIO;
}
return 0;
}