From 9fc3e2178c227fdf4ff7195d6703d3cc6a3cfb6f Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Sun, 25 Oct 2015 03:37:45 -0700 Subject: [PATCH] msm: mdss: hdmi: proper state validation and buffer handling Validate current hdcp lib state before starting new hdcp session. Double buffer the sink message and protect its access with mutex to handle fast re-authentication requests. Change-Id: I039b1a0c818a2e66eef583afb15420ce8587a75c Signed-off-by: Ajay Singh Parmar --- drivers/misc/hdcp.c | 128 +++++++++++++++++--- drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c | 45 +++++-- 2 files changed, 146 insertions(+), 27 deletions(-) diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 74449d8186d9..0a32d11f6161 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -337,6 +337,7 @@ struct hdcp_lib_handle { void *client_ctx; struct hdcp_client_ops *client_ops; struct mutex hdcp_lock; + struct mutex msg_lock; struct mutex wakeup_mutex; enum hdcp_state hdcp_state; enum hdcp_lib_wakeup_cmd wakeup_cmd; @@ -755,6 +756,77 @@ exit: return supported; } +static void hdcp_lib_check_worker_status(struct hdcp_lib_handle *handle) +{ + if (!list_empty(&handle->init.node)) + pr_debug("init work queued\n"); + + if (handle->worker.current_work == &handle->init) + pr_debug("init work executing\n"); + + if (!list_empty(&handle->msg_sent.node)) + pr_debug("msg_sent work queued\n"); + + if (handle->worker.current_work == &handle->msg_sent) + pr_debug("msg_sent work executing\n"); + + if (!list_empty(&handle->msg_recvd.node)) + pr_debug("msg_recvd work queued\n"); + + if (handle->worker.current_work == &handle->msg_recvd) + pr_debug("msg_recvd work executing\n"); + + if (!list_empty(&handle->timeout.node)) + pr_debug("timeout work queued\n"); + + if (handle->worker.current_work == &handle->timeout) + pr_debug("timeout work executing\n"); + + if (!list_empty(&handle->clean.node)) + pr_debug("clean work queued\n"); + + if (handle->worker.current_work == &handle->clean) + pr_debug("clean work executing\n"); + + if (!list_empty(&handle->topology.node)) + pr_debug("topology work queued\n"); + + if (handle->worker.current_work == &handle->topology) + pr_debug("topology work executing\n"); + + if (!list_empty(&handle->stream.node)) + pr_debug("stream work queued\n"); + + if (handle->worker.current_work == &handle->stream) + pr_debug("stream work executing\n"); +} + +static int hdcp_lib_check_valid_state(struct hdcp_lib_handle *handle) +{ + int rc = 0; + + if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) { + if (!list_empty(&handle->worker.work_list)) { + hdcp_lib_check_worker_status(handle); + rc = -EBUSY; + goto exit; + } + } else { + if (atomic_read(&handle->hdcp_off)) { + pr_warn("hdcp2.2 session tearing down\n"); + goto exit; + } + + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("hdcp 2.2 app not loaded\n"); + rc = -EINVAL; + goto exit; + } + } +exit: + return rc; +} + static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) { struct hdcp_lib_handle *handle; @@ -770,22 +842,29 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) mutex_lock(&handle->wakeup_mutex); handle->wakeup_cmd = data->cmd; + pr_debug("%s\n", hdcp_lib_cmd_to_str(handle->wakeup_cmd)); - pr_debug("cmd: %s\n", hdcp_lib_cmd_to_str(handle->wakeup_cmd)); + rc = hdcp_lib_check_valid_state(handle); + if (rc) + goto exit; + mutex_lock(&handle->msg_lock); if (data->recvd_msg_len) { - handle->last_msg_recvd_len = data->recvd_msg_len; + kzfree(handle->last_msg_recvd_buf); + handle->last_msg_recvd_len = data->recvd_msg_len; handle->last_msg_recvd_buf = kzalloc(data->recvd_msg_len, GFP_KERNEL); if (!handle->last_msg_recvd_buf) { rc = -ENOMEM; + mutex_unlock(&handle->msg_lock); goto exit; } memcpy(handle->last_msg_recvd_buf, data->recvd_msg_buf, data->recvd_msg_len); } + mutex_unlock(&handle->msg_lock); if (!completion_done(&handle->topo_wait)) complete_all(&handle->topo_wait); @@ -807,32 +886,27 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) case HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS: handle->last_msg_sent = handle->listener_buf[0]; - if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->msg_sent); + queue_kthread_work(&handle->worker, &handle->msg_sent); break; case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED: case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED: - if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->clean); + queue_kthread_work(&handle->worker, &handle->clean); break; case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS: - if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->msg_recvd); + queue_kthread_work(&handle->worker, &handle->msg_recvd); break; case HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT: - if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->timeout); + queue_kthread_work(&handle->worker, &handle->timeout); break; case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE: - if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->stream); + queue_kthread_work(&handle->worker, &handle->stream); break; default: pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); } exit: mutex_unlock(&handle->wakeup_mutex); - return 0; + return rc; } static void hdcp_lib_msg_sent_work(struct kthread_work *work) @@ -926,7 +1000,6 @@ exit: if (rc && !atomic_read(&handle->hdcp_off)) queue_kthread_work(&handle->worker, &handle->clean); - return; } static void hdcp_lib_manage_timeout_work(struct kthread_work *work) @@ -1044,18 +1117,29 @@ static void hdcp_lib_msg_recvd_work(struct kthread_work *work) } mutex_lock(&handle->hdcp_lock); - - msg = handle->last_msg_recvd_buf; - msglen = handle->last_msg_recvd_len; - cdata.context = handle->client_ctx; + mutex_lock(&handle->msg_lock); + msglen = handle->last_msg_recvd_len; + if (msglen <= 0) { pr_err("invalid msg len\n"); + mutex_unlock(&handle->msg_lock); rc = -EINVAL; goto exit; } + msg = kzalloc(msglen, GFP_KERNEL); + if (!msg) { + mutex_unlock(&handle->msg_lock); + rc = -ENOMEM; + goto exit; + } + + memcpy(msg, handle->last_msg_recvd_buf, msglen); + + mutex_unlock(&handle->msg_lock); + pr_debug("msg received: %s from sink\n", hdcp_lib_message_name((int)msg[0])); @@ -1140,7 +1224,7 @@ static void hdcp_lib_msg_recvd_work(struct kthread_work *work) } exit: - kzfree(handle->last_msg_recvd_buf); + kzfree(msg); mutex_unlock(&handle->hdcp_lock); hdcp_lib_wakeup_client(handle, &cdata); @@ -1160,7 +1244,6 @@ static void hdcp_lib_topology_work(struct kthread_work *work) return; } - reinit_completion(&handle->topo_wait); timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3); if (!timeout) { @@ -1185,6 +1268,9 @@ bool hdcp1_check_if_supported_load_app(void) } } + pr_debug("hdcp1 app %s loaded\n", + hdcp1_supported ? "successfully" : "not"); + return hdcp1_supported; } @@ -1274,6 +1360,7 @@ int hdcp_library_register(void **pphdcpcontext, atomic_set(&handle->hdcp_off, 0); mutex_init(&handle->hdcp_lock); + mutex_init(&handle->msg_lock); mutex_init(&handle->wakeup_mutex); init_kthread_worker(&handle->worker); @@ -1326,6 +1413,7 @@ void hdcp_library_deregister(void *phdcpcontext) kthread_stop(handle->thread); kzfree(handle->qseecom_handle); + kzfree(handle->last_msg_recvd_buf); mutex_destroy(&handle->hdcp_lock); mutex_destroy(&handle->wakeup_mutex); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index b9e70b042b62..565fa5661bdd 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -56,6 +56,7 @@ struct hdmi_hdcp2p2_ctrl { enum hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */ struct hdmi_hdcp_init_data init_data; /* Feature data from HDMI drv */ struct mutex mutex; /* mutex to protect access to ctrl */ + struct mutex msg_lock; /* mutex to protect access to msg buffer */ struct mutex wakeup_mutex; /* mutex to protect access to wakeup call*/ struct hdmi_hdcp_ops *ops; void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */ @@ -99,18 +100,24 @@ static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) ctrl->wakeup_cmd = data->cmd; ctrl->timeout = data->timeout; - if (data->send_msg_len) { + mutex_lock(&ctrl->msg_lock); + if (data->send_msg_len) { ctrl->send_msg_len = data->send_msg_len; + kzfree(ctrl->send_msg_buf); + ctrl->send_msg_buf = kzalloc( data->send_msg_len, GFP_KERNEL); - if (!ctrl->send_msg_buf) + if (!ctrl->send_msg_buf) { + mutex_unlock(&ctrl->msg_lock); goto exit; + } memcpy(ctrl->send_msg_buf, data->send_msg_buf, ctrl->send_msg_len); } + mutex_unlock(&ctrl->msg_lock); switch (ctrl->wakeup_cmd) { case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: @@ -137,7 +144,7 @@ exit: return 0; } -static inline void hdmi_hdcp2p2_wakeup_lib(struct hdmi_hdcp2p2_ctrl *ctrl, +static inline int hdmi_hdcp2p2_wakeup_lib(struct hdmi_hdcp2p2_ctrl *ctrl, struct hdcp_lib_wakeup_data *data) { int rc = 0; @@ -149,6 +156,8 @@ static inline void hdmi_hdcp2p2_wakeup_lib(struct hdmi_hdcp2p2_ctrl *ctrl, pr_err("error sending %s to lib\n", hdcp_lib_cmd_to_str(data->cmd)); } + + return rc; } static void hdmi_hdcp2p2_reset(struct hdmi_hdcp2p2_ctrl *ctrl) @@ -461,6 +470,8 @@ static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, struct hdmi_hdcp2p2_ctrl, send_msg); struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + char *msg; + uint32_t msglen; if (!ctrl) { pr_err("invalid input\n"); @@ -476,9 +487,25 @@ static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) goto exit; } + mutex_lock(&ctrl->msg_lock); + msglen = ctrl->send_msg_len; + + if (!msglen) { + mutex_unlock(&ctrl->msg_lock); + goto exit; + } + + msg = kzalloc(msglen, GFP_KERNEL); + if (!msg) { + mutex_unlock(&ctrl->msg_lock); + goto exit; + } + + memcpy(msg, ctrl->send_msg_buf, msglen); + mutex_unlock(&ctrl->msg_lock); + /* Forward the message to the sink */ - rc = hdmi_hdcp2p2_ddc_write_message(ctrl, - ctrl->send_msg_buf, (size_t)ctrl->send_msg_len); + rc = hdmi_hdcp2p2_ddc_write_message(ctrl, msg, (size_t)msglen); if (rc) { pr_err("Error sending msg to sink %d\n", rc); cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED; @@ -486,7 +513,7 @@ static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS; } exit: - kzfree(ctrl->send_msg_buf); + kzfree(msg); mutex_unlock(&ctrl->mutex); hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); @@ -755,7 +782,8 @@ static void hdmi_hdcp2p2_auth_work(struct kthread_work *work) mutex_unlock(&ctrl->mutex); - hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + if (hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata)) + hdmi_hdcp2p2_auth_failed(ctrl); } void hdmi_hdcp2p2_deinit(void *input) @@ -778,6 +806,8 @@ void hdmi_hdcp2p2_deinit(void *input) &hdmi_hdcp2p2_fs_attr_group); mutex_destroy(&ctrl->mutex); + mutex_destroy(&ctrl->msg_lock); + mutex_destroy(&ctrl->wakeup_mutex); kfree(ctrl); } @@ -832,6 +862,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) ctrl->ops = &ops; mutex_init(&ctrl->mutex); + mutex_init(&ctrl->msg_lock); mutex_init(&ctrl->wakeup_mutex); rc = hdcp_library_register(&ctrl->lib_ctx,