lineage_kernel_xcoverpro/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2.c

438 lines
11 KiB
C
Executable File

/*
* drivers/soc/samsung/exynos-hdcp/exynos-hdcp.c
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/smc.h>
#include <asm/cacheflush.h>
#include <linux/smc.h>
#include "iia_link/exynos-hdcp2-iia-auth.h"
#include "exynos-hdcp2-teeif.h"
#include "iia_link/exynos-hdcp2-iia-selftest.h"
#include "exynos-hdcp2-encrypt.h"
#include "exynos-hdcp2-log.h"
#include "dp_link/exynos-hdcp2-dplink-if.h"
#include "dp_link/exynos-hdcp2-dplink.h"
#include "dp_link/exynos-hdcp2-dplink-selftest.h"
#define EXYNOS_HDCP_DEV_NAME "hdcp2"
struct miscdevice hdcp;
static DEFINE_MUTEX(hdcp_lock);
struct hdcp_session_list g_hdcp_session_list;
enum hdcp_result hdcp_link_ioc_authenticate(void);
static uint32_t inst_num;
static long hdcp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int rval;
switch (cmd) {
#if defined(CONFIG_HDCP2_IIA_ENABLE)
case (uint32_t)HDCP_IOC_SESSION_OPEN:
{
struct hdcp_sess_info ss_info;
mutex_lock(&hdcp_lock);
rval = copy_from_user(&ss_info, (void __user *)arg, sizeof(struct hdcp_sess_info));
if (rval) {
hdcp_err("Session open copy from user fail. (%x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
rval = hdcp_session_open(&ss_info);
if (rval) {
hdcp_err("Session open fail. (%x)\n", rval);
mutex_unlock(&hdcp_lock);
return rval;
}
rval = copy_to_user((void __user *)arg, &ss_info, sizeof(struct hdcp_sess_info));
if (rval) {
hdcp_err("Session open copy to user fail. (%x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_TO_USER_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
case (uint32_t)HDCP_IOC_SESSION_CLOSE:
{
/* todo: session close */
struct hdcp_sess_info ss_info;
mutex_lock(&hdcp_lock);
rval = copy_from_user(&ss_info, (void __user *)arg, sizeof(struct hdcp_sess_info));
if (rval) {
hdcp_err("Session close copy from user fail. (%x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
rval = hdcp_session_close(&ss_info);
if (rval) {
hdcp_err("hdcp_session close fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_SESSION_CLOSE_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
case (uint32_t)HDCP_IOC_LINK_OPEN:
{
struct hdcp_link_info lk_info;
mutex_lock(&hdcp_lock);
rval = copy_from_user(&lk_info, (void __user *)arg, sizeof(struct hdcp_link_info));
if (rval) {
hdcp_err("hdcp_link open copy from user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
rval = hdcp_link_open(&lk_info, HDCP_LINK_TYPE_IIA);
if (rval) {
hdcp_err("hdcp_link open fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_LINK_OPEN_FAILED;
}
rval = copy_to_user((void __user *)arg, &lk_info, sizeof(struct hdcp_link_info));
if (rval) {
hdcp_err("hdcp_link open copy to user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_TO_USER_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
case (uint32_t)HDCP_IOC_LINK_CLOSE:
{
struct hdcp_link_info lk_info;
mutex_lock(&hdcp_lock);
/* find Session node which contain the Link */
rval = copy_from_user(&lk_info, (void __user *)arg, sizeof(struct hdcp_link_info));
if (rval) {
hdcp_err("hdcp_link close copy from user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
rval = hdcp_link_close(&lk_info);
if (rval) {
hdcp_err("hdcp_link close fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_LINK_CLOSE_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
case (uint32_t)HDCP_IOC_LINK_AUTH:
{
struct hdcp_msg_info msg_info;
mutex_lock(&hdcp_lock);
rval = copy_from_user(&msg_info, (void __user *)arg, sizeof(struct hdcp_msg_info));
if (rval) {
hdcp_err("hdcp_authenticate fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
rval = hdcp_link_authenticate(&msg_info);
if (rval) {
hdcp_err("hdcp_authenticate fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_AUTHENTICATE_FAILED;
}
rval = copy_to_user((void __user *)arg, &msg_info, sizeof(struct hdcp_msg_info));
if (rval) {
hdcp_err("hdcp_authenticate fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_TO_USER_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
case (uint32_t)HDCP_IOC_LINK_ENC:
{
/* todo: link close */
struct hdcp_enc_info enc_info;
size_t packet_len = 0;
uint8_t pes_priv[HDCP_PRIVATE_DATA_LEN];
unsigned long input_phys, output_phys;
struct hdcp_session_node *ss_node;
struct hdcp_link_node *lk_node;
struct hdcp_link_data *lk_data;
uint8_t *input_virt, *output_virt;
int ret = HDCP_SUCCESS;
mutex_lock(&hdcp_lock);
/* find Session node which contain the Link */
rval = copy_from_user(&enc_info, (void __user *)arg, sizeof(struct hdcp_enc_info));
if (rval) {
hdcp_err("hdcp_encrypt copy from user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
/* find Session node which contains the Link */
ss_node = hdcp_session_list_find(enc_info.id, &g_hdcp_session_list);
if (!ss_node) {
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_INVALID_INPUT;
}
lk_node = hdcp_link_list_find(enc_info.id, &ss_node->ss_data->ln);
if (!lk_node) {
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_INVALID_INPUT;
}
lk_data = lk_node->lk_data;
if (!lk_data) {
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_INVALID_INPUT;
}
input_phys = (unsigned long)enc_info.input_phys;
output_phys = (unsigned long)enc_info.output_phys;
/* set input counters */
memset(&(lk_data->tx_ctx.input_ctr), 0x00, HDCP_INPUT_CTR_LEN);
/* set output counters */
memset(&(lk_data->tx_ctx.str_ctr), 0x00, HDCP_STR_CTR_LEN);
packet_len = (size_t)enc_info.input_len;
input_virt = (uint8_t *)phys_to_virt(input_phys);
output_virt = (uint8_t *)phys_to_virt(output_phys);
__flush_dcache_area(input_virt, packet_len);
__flush_dcache_area(output_virt, packet_len);
ret = encrypt_packet(pes_priv,
input_phys, packet_len,
output_phys, packet_len,
&(lk_data->tx_ctx));
if (ret) {
hdcp_err("encrypt_packet() is failed with 0x%x\n", ret);
mutex_unlock(&hdcp_lock);
return -1;
}
rval = copy_to_user((void __user *)arg, &enc_info, sizeof(struct hdcp_enc_info));
if (rval) {
hdcp_err("hdcp_encrypt copy to user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_TO_USER_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
case (uint32_t)HDCP_IOC_STREAM_MANAGE:
{
struct hdcp_stream_info stream_info;
mutex_lock(&hdcp_lock);
rval = copy_from_user(&stream_info, (void __user *)arg, sizeof(struct hdcp_stream_info));
if (rval) {
hdcp_err("hdcp_link stream manage copy from user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
rval = hdcp_link_stream_manage(&stream_info);
if (rval) {
hdcp_err("hdcp_link stream manage fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_LINK_STREAM_FAILED;
}
rval = copy_to_user((void __user *)arg, &stream_info, sizeof(struct hdcp_stream_info));
if (rval) {
hdcp_err("hdcp_link stream manage copy to user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_TO_USER_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
#endif
#if defined(CONFIG_HDCP2_EMULATION_MODE)
case (uint32_t)HDCP_IOC_EMUL_DPLINK_TX:
{
uint32_t emul_cmd;
if (copy_from_user(&emul_cmd, (void __user *)arg, sizeof(uint32_t)))
return -EINVAL;
return dplink_emul_handler(emul_cmd);
}
#endif
case (uint32_t)HDCP_IOC_DPLINK_TX_AUTH:
{
#if defined(CONFIG_HDCP2_DP_ENABLE)
rval = dp_hdcp_protocol_self_test();
if (rval) {
hdcp_err("DP self_test fail. errno(%d)\n", rval);
return rval;
}
hdcp_err("DP self_test success!!\n");
#endif
#if defined(TEST_VECTOR_2)
/* todo: support test vector 1 */
rval = iia_hdcp_protocol_self_test();
if (rval) {
hdcp_err("IIA self_test failed. errno(%d)\n", rval);
return rval;
}
hdcp_err("IIA self_test success!!\n");
#endif
rval = 0;
return rval;
}
case (uint32_t)HDCP_IOC_WRAP_KEY:
{
struct hdcp_wrapped_key key_info;
mutex_lock(&hdcp_lock);
rval = copy_from_user(&key_info, (void __user *)arg, sizeof(struct hdcp_wrapped_key));
if (rval) {
hdcp_err("hdcp_wrap_key copy from user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_FROM_USER_FAILED;
}
if (hdcp_wrap_key(&key_info)) {
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_WRAP_FAIL;
}
rval = copy_to_user((void __user *)arg, &key_info, sizeof(struct hdcp_wrapped_key));
if (rval) {
hdcp_err("hdcp_wrap_key copy to user fail (0x%08x)\n", rval);
mutex_unlock(&hdcp_lock);
return HDCP_ERROR_COPY_TO_USER_FAILED;
}
mutex_unlock(&hdcp_lock);
break;
}
default:
hdcp_err("HDCP: Invalid IOC num(%d)\n", cmd);
return -ENOTTY;
}
return 0;
}
static int hdcp_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct device *dev = miscdev->this_device;
struct hdcp_info *info;
info = kzalloc(sizeof(struct hdcp_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->dev = dev;
file->private_data = info;
mutex_lock(&hdcp_lock);
inst_num++;
/* todo: hdcp device initialize ? */
mutex_unlock(&hdcp_lock);
return 0;
}
static int hdcp_release(struct inode *inode, struct file *file)
{
struct hdcp_info *info = file->private_data;
/* disable drm if we were the one to turn it on */
mutex_lock(&hdcp_lock);
inst_num--;
/* todo: hdcp device finalize ? */
mutex_unlock(&hdcp_lock);
kfree(info);
return 0;
}
static int __init hdcp_init(void)
{
int ret;
hdcp_info("hdcp2 driver init\n");
ret = misc_register(&hdcp);
if (ret) {
hdcp_err("hdcp can't register misc on minor=%d\n",
MISC_DYNAMIC_MINOR);
return ret;
}
/* todo: do initialize sequence */
hdcp_session_list_init(&g_hdcp_session_list);
#if defined(CONFIG_HDCP2_DP_ENABLE)
if (hdcp_dplink_init() < 0) {
hdcp_err("hdcp_dplink_init fail\n");
return -EINVAL;
}
#endif
ret = hdcp_tee_open();
if (ret) {
hdcp_err("hdcp_tee_open fail\n");
return -EINVAL;
}
return 0;
}
static void __exit hdcp_exit(void)
{
/* todo: do clear sequence */
misc_deregister(&hdcp);
hdcp_session_list_destroy(&g_hdcp_session_list);
hdcp_tee_close();
}
static const struct file_operations hdcp_fops = {
.owner = THIS_MODULE,
.open = hdcp_open,
.release = hdcp_release,
.compat_ioctl = hdcp_ioctl,
.unlocked_ioctl = hdcp_ioctl,
};
struct miscdevice hdcp = {
.minor = MISC_DYNAMIC_MINOR,
.name = EXYNOS_HDCP_DEV_NAME,
.fops = &hdcp_fops,
};
module_init(hdcp_init);
module_exit(hdcp_exit);