diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index e42d4fb5587e..3acfb3e272cf 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -28,8 +28,8 @@ #include #include #include -#include #include +#include #include "qseecom_kernel.h" @@ -312,54 +312,58 @@ struct __attribute__ ((__packed__)) repeater_info_struct { }; /* - * struct hdcp2p2_handle - handle for hdcp client + * struct hdcp_lib_handle - handle for hdcp client * @qseecom_handle - for sending commands to qseecom - * @hdcp_workqueue - work queue for hdcp thread - * @auth_work - work placed in the work queue * @listener_buf - buffer containing message shared with the client * @msglen - size message in the buffer * @tz_ctxhandle - context handle shared with tz * @hdcp_timeout - timeout in msecs shared for hdcp messages - * @ske_flag - flag to indicate that msg from tz is ske_send_eks * @client_ctx - client context maintained by hdmi * @client_ops - handle to call APIs exposed by hdcp client * @timeout_lock - this lock protects hdcp_timeout field * @msg_lock - this lock protects the message buffer */ -struct hdcp2p2_handle { - struct workqueue_struct *hdcp_workqueue; - struct work_struct auth_work; +struct hdcp_lib_handle { unsigned char *listener_buf; uint32_t msglen; uint32_t tz_ctxhandle; uint32_t hdcp_timeout; - bool ske_flag; bool no_stored_km_flag; void *client_ctx; struct hdcp_client_ops *client_ops; struct mutex hdcp_lock; enum hdcp_state hdcp_state; + enum hdcp_lib_wakeup_cmd wakeup_cmd; bool repeater_flag; struct qseecom_handle *qseecom_handle; - uint32_t ref_cnt; - atomic_t hdcp_off; + int last_msg_sent; + char *last_msg_recvd_buf; + uint32_t last_msg_recvd_len; + atomic_t hdcp_off_pending; + + struct task_struct *thread; + + struct kthread_worker worker; + struct kthread_work init; + struct kthread_work msg_sent; + struct kthread_work msg_recvd; + struct kthread_work timeout; + struct kthread_work clean; }; -struct hdcp2p2_message_map { +struct hdcp_lib_message_map { int msg_id; const char *msg_name; }; -static void hdcp2p2_cleanup(void *phdcpcontext); - -static const char *hdcp2p2_message_name(int msg_id) +static const char *hdcp_lib_message_name(int msg_id) { /* * Message ID map. The first number indicates the message number * assigned to the message by the HDCP 2.2 spec. This is also the first * byte of every HDCP 2.2 authentication protocol message. */ - static struct hdcp2p2_message_map hdcp2p2_msg_map[] = { + static struct hdcp_lib_message_map hdcp_lib_msg_map[] = { {2, "AKE_INIT"}, {3, "AKE_SEND_CERT"}, {4, "AKE_NO_STORED_KM"}, @@ -376,283 +380,115 @@ static const char *hdcp2p2_message_name(int msg_id) }; int i; - for (i = 0; i < ARRAY_SIZE(hdcp2p2_msg_map); i++) { - if (msg_id == hdcp2p2_msg_map[i].msg_id) - return hdcp2p2_msg_map[i].msg_name; + for (i = 0; i < ARRAY_SIZE(hdcp_lib_msg_map); i++) { + if (msg_id == hdcp_lib_msg_map[i].msg_id) + return hdcp_lib_msg_map[i].msg_name; } return "UNKNOWN"; } -static int hdcp2p2_manage_timeout(struct hdcp2p2_handle *handle) +static inline int hdcp_lib_report_error(struct hdcp_lib_handle *handle) { - int rc = 0; - struct hdcp_send_timeout_req *req_buf; - struct hdcp_send_timeout_rsp *rsp_buf; + int rc = -EINVAL; - if (!handle) { - pr_err("invalid input\n"); - rc = -EINVAL; - goto exit; + if (handle->client_ops->wakeup) { + rc = handle->client_ops->wakeup( + handle->client_ctx, HDMI_HDCP_STATUS_FAIL, + 0, 0, 0); + if (rc) + pr_err("error: report error\n"); + } else { + pr_err("error: client ops wakeup not defined\n"); } - req_buf = (struct hdcp_send_timeout_req *) - (handle->qseecom_handle->sbuf); - req_buf->commandid = HDCP_TXMTR_SEND_MESSAGE_TIMEOUT; - req_buf->ctxhandle = handle->tz_ctxhandle; - - rsp_buf = (struct hdcp_send_timeout_rsp *)(handle-> - qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( - struct hdcp_send_timeout_req))); - - rc = qseecom_send_command(handle->qseecom_handle, req_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_req)), rsp_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_rsp))); - - if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS)) { - pr_err("qseecom cmd failed for with err = %d status = %d\n", - rc, rsp_buf->status); - rc = -EINVAL; - goto exit; - } - - if (rsp_buf->commandid == HDCP_TXMTR_SEND_MESSAGE_TIMEOUT) { - pr_err("HDCP_TXMTR_SEND_MESSAGE_TIMEOUT\n"); - rc = -EAGAIN; - goto exit; - } - - /* - * if the response contains LC_Init message - * send the message again to TZ - */ - if ((rsp_buf->commandid == HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) && - ((int)rsp_buf->message[0] == LC_INIT_MESSAGE_ID) && - (rsp_buf->msglen == LC_INIT_MESSAGE_SIZE)) { - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - } -exit: return rc; } -static int hdcp2p2_enable_encryption(struct hdcp2p2_handle *handle) +static inline int hdcp_lib_recv_message(struct hdcp_lib_handle *handle) +{ + int rc = -EINVAL; + + if (handle->client_ops->wakeup) { + rc = handle->client_ops->wakeup( + handle->client_ctx, HDMI_HDCP_RECV_MESSAGE, + 0, 0, handle->hdcp_timeout); + if (rc) + pr_err("error receiving message from client\n"); + } else { + pr_err("error: client ops wakeup not defined\n"); + } + + return rc; +} + +static inline int hdcp_lib_send_message(struct hdcp_lib_handle *handle) +{ + int rc = -EINVAL; + + pr_debug("send msg: %s to sink\n", + hdcp_lib_message_name((int)handle->listener_buf[0])); + + if (handle->client_ops->wakeup) { + rc = handle->client_ops->wakeup( + handle->client_ctx, HDMI_HDCP_SEND_MESSAGE, + handle->listener_buf, handle->msglen, 0); + if (!rc) + handle->last_msg_sent = (int)handle->listener_buf[0]; + else + pr_err("error sending message to client\n"); + } else { + pr_err("error: client ops wakeup not defined\n"); + } + + return rc; +} + +static int hdcp_lib_enable_encryption(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_set_hw_key_req *req_buf; struct hdcp_set_hw_key_rsp *rsp_buf; - uint32_t timeout = 0; - if (handle->ske_flag) { - /* - * wait for 200ms before enabling encryption - * as per hdcp2p2 sepcifications. - */ - msleep(SLEEP_SET_HW_KEY_MS); + /* + * wait for 200ms before enabling encryption + * as per hdcp2p2 sepcifications. + */ + msleep(SLEEP_SET_HW_KEY_MS); - req_buf = (struct hdcp_set_hw_key_req *)( - handle->qseecom_handle->sbuf); - req_buf->commandid = HDCP_TXMTR_SET_HW_KEY; - req_buf->ctxhandle = handle->tz_ctxhandle; - - rsp_buf = (struct hdcp_set_hw_key_rsp *)( - handle->qseecom_handle->sbuf + QSEECOM_ALIGN( - sizeof(struct hdcp_set_hw_key_req))); - - rc = qseecom_send_command(handle->qseecom_handle, req_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_set_hw_key_req)), - rsp_buf, QSEECOM_ALIGN(sizeof( - struct hdcp_set_hw_key_rsp))); - - if ((rc < 0) || (rsp_buf->status < 0)) { - pr_err("qseecom cmd failed with err = %d status = %d\n", - rc, rsp_buf->status); - timeout = -1; - goto exit; - } - - /* reached an authenticated state */ - handle->hdcp_state |= HDCP_STATE_AUTHENTICATED; - - /* if not a repeater then there is no need to start the timer */ - if (!handle->repeater_flag) - goto exit; - - /* set the timeout value to the actual - 200ms */ - timeout = handle->hdcp_timeout - SLEEP_SET_HW_KEY_MS; - } -exit: - return timeout; -} - -static int hdcp2p2_send_message(struct hdcp2p2_handle *handle) -{ - int rc = -EINVAL; - char *rcvr_buf; - uint32_t timeout; - - if (!handle) { - pr_err("invalid input\n"); - goto exit; - } - - if (!handle->listener_buf) { - pr_err("no message to copy\n"); - goto exit; - } - - if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { - pr_err("hdcp library not loaded\n"); - goto exit; - } - - if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { - pr_err("txmtr is not initialized\n"); - goto exit; - } - - rcvr_buf = kzalloc(MAX_TX_MESSAGE_SIZE, GFP_KERNEL); - if (!rcvr_buf) - goto exit; - - memcpy(rcvr_buf, handle->listener_buf, MAX_TX_MESSAGE_SIZE); - - timeout = handle->hdcp_timeout; - - if (atomic_read(&handle->hdcp_off)) { - pr_err("send msg check: hdcp off underway\n"); - rc = 0; - goto exit; - } - - if (handle->client_ops->hdcp_send_message) { - rc = handle->client_ops->hdcp_send_message( - handle->client_ctx, - rcvr_buf, handle->msglen); - if (rc) { - pr_err("error sending message to client\n"); - goto exit; - } - } else { - pr_err("send_msg client ops not defined\n"); - goto exit; - } - - rc = hdcp2p2_enable_encryption(handle); - if (rc < 0) { - pr_err("error enabling ecryption\n"); - goto exit; - } else if (rc > 0) { - timeout = rc; - } - - if (handle->hdcp_timeout == 0) { - rc = 0; - goto exit; - } - - if (atomic_read(&handle->hdcp_off)) { - pr_err("recv msg check: hdcp off underway\n"); - rc = 0; - goto exit; - } - - if (handle->client_ops->hdcp_recv_message) { - rc = handle->client_ops->hdcp_recv_message( - handle->client_ctx, rcvr_buf, - handle->msglen, timeout); - if (rc == -ETIMEDOUT) { - pr_err("msg read time %dms expired\n", timeout); - rc = hdcp2p2_manage_timeout(handle); - } - } else { - pr_err("recv_msg client ops not defined\n"); - } -exit: - kzfree(rcvr_buf); - - if (rc) - hdcp2p2_cleanup(handle); - - return rc; -} - -static void hdcp2p2_authenticate_work(struct work_struct *work) -{ - struct hdcp2p2_handle *handle = container_of(work, - struct hdcp2p2_handle, auth_work); - - hdcp2p2_send_message(handle); -} - -static int hdcp2p2_txmtr_query_stream_type(void *phdcpcontext) -{ - int rc = 0; - struct hdcp_query_stream_type_req *req_buf; - struct hdcp_query_stream_type_rsp *rsp_buf; - struct hdcp2p2_handle *handle = phdcpcontext; - - if (!handle) { - pr_err("invalid input\n"); - return -EINVAL; - } - - if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { - pr_debug("hdcp library not loaded\n"); - goto end; - } - - if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { - pr_err("txmtr is not initialized\n"); - rc = -EINVAL; - goto end; - } - - flush_work(&handle->auth_work); - - /* send command to TZ */ - req_buf = (struct hdcp_query_stream_type_req *)handle-> - qseecom_handle->sbuf; - req_buf->commandid = HDCP_TXMTR_QUERY_STREAM_TYPE; + req_buf = (struct hdcp_set_hw_key_req *)( + handle->qseecom_handle->sbuf); + req_buf->commandid = HDCP_TXMTR_SET_HW_KEY; req_buf->ctxhandle = handle->tz_ctxhandle; - rsp_buf = (struct hdcp_query_stream_type_rsp *)(handle-> - qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( - struct hdcp_query_stream_type_req))); + + rsp_buf = (struct hdcp_set_hw_key_rsp *)( + handle->qseecom_handle->sbuf + QSEECOM_ALIGN( + sizeof(struct hdcp_set_hw_key_req))); rc = qseecom_send_command(handle->qseecom_handle, req_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_req)), rsp_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_rsp))); + QSEECOM_ALIGN(sizeof(struct hdcp_set_hw_key_req)), + rsp_buf, QSEECOM_ALIGN(sizeof( + struct hdcp_set_hw_key_rsp))); - if ((rc < 0) || (rsp_buf->status < 0) || (rsp_buf->msglen <= 0) || - (rsp_buf->commandid != HDCP_TXMTR_QUERY_STREAM_TYPE) || - (rsp_buf->msg == NULL)) { - pr_err("qseecom cmd failed with err=%d status=%d\n", + if ((rc < 0) || (rsp_buf->status < 0)) { + pr_err("qseecom cmd failed with err = %d status = %d\n", rc, rsp_buf->status); - rc = -1; - goto end; + rc = -EINVAL; + goto error; } - pr_debug("message received is %s\n", - hdcp2p2_message_name((int)rsp_buf->msg[0])); + /* reached an authenticated state */ + handle->hdcp_state |= HDCP_STATE_AUTHENTICATED; - memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); - memcpy(handle->listener_buf, (unsigned char *)rsp_buf->msg, - rsp_buf->msglen); - handle->hdcp_timeout = rsp_buf->timeout; - handle->msglen = rsp_buf->msglen; + pr_debug("success\n"); + return 0; +error: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - - pr_debug("hdcp txmtr query stream type success\n"); -end: return rc; } -static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) +static int hdcp_lib_library_load(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_init_req *req_buf; @@ -660,12 +496,12 @@ static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) if (!handle) { pr_err("invalid input\n"); - goto end; + goto exit; } - if (handle->ref_cnt > 0) { - pr_err("error: ref_cnt %d, should be 0\n", handle->ref_cnt); - goto end; + if (handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_err("library already loaded\n"); + return rc; } /* @@ -676,7 +512,7 @@ static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) TZAPP_NAME, QSEECOM_SBUFF_SIZE); if (rc) { pr_err("qseecom_start_app failed %d\n", rc); - goto end; + goto exit; } pr_debug("qseecom_start_app success\n"); @@ -693,18 +529,17 @@ static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) if (rc < 0) { pr_err("qseecom cmd failed err = %d\n", rc); - goto end; + goto exit; } - pr_debug("loading secure app success\n"); + pr_debug("success\n"); - handle->ref_cnt++; handle->hdcp_state |= HDCP_STATE_APP_LOADED; -end: +exit: return rc; } -static int hdcp2p2_library_unload(struct hdcp2p2_handle *handle) +static int hdcp_lib_library_unload(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_deinit_req *req_buf; @@ -712,12 +547,12 @@ static int hdcp2p2_library_unload(struct hdcp2p2_handle *handle) if (!handle) { pr_err("invalid input\n"); - goto end; + goto exit; } - if (!handle->ref_cnt) { - pr_err("error: ref_cnt 0\n"); - goto end; + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("library not loaded\n"); + return rc; } /* unloading app by sending hdcp_lib_deinit cmd */ @@ -733,26 +568,23 @@ static int hdcp2p2_library_unload(struct hdcp2p2_handle *handle) if (rc < 0) { pr_err("qseecom cmd failed err = %d\n", rc); - goto end; + goto exit; } /* deallocate the resources for qseecom handle */ rc = qseecom_shutdown_app(&handle->qseecom_handle); if (rc) { pr_err("qseecom_shutdown_app failed err: %d\n", rc); - goto end; + goto exit; } - pr_debug("unloading secure app success\n"); - - handle->ref_cnt--; handle->hdcp_state &= ~HDCP_STATE_APP_LOADED; -end: - + pr_debug("success\n"); +exit: return rc; } -static int hdcp2p2_txmtr_init(struct hdcp2p2_handle *handle) +static int hdcp_lib_txmtr_init(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_init_req *req_buf; @@ -789,10 +621,14 @@ static int hdcp2p2_txmtr_init(struct hdcp2p2_handle *handle) (rsp_buf->msglen <= 0) || (rsp_buf->message == NULL)) { pr_err("qseecom cmd failed with err = %d, status = %d\n", rc, rsp_buf->status); - rc = -1; + rc = -EINVAL; goto exit; } + pr_debug("recvd %s from TZ at %dms\n", + hdcp_lib_message_name((int)rsp_buf->message[0]), + jiffies_to_msecs(jiffies)); + /* send the response to HDMI driver */ memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); memcpy(handle->listener_buf, (unsigned char *)rsp_buf->message, @@ -803,17 +639,12 @@ static int hdcp2p2_txmtr_init(struct hdcp2p2_handle *handle) handle->tz_ctxhandle = rsp_buf->ctxhandle; handle->hdcp_state |= HDCP_STATE_TXMTR_INIT; - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - - pr_debug("hdcp txmtr successfully initialized\n"); + pr_debug("success\n"); exit: return rc; } -static int hdcp2p2_txmtr_deinit(struct hdcp2p2_handle *handle) +static int hdcp_lib_txmtr_deinit(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_deinit_req *req_buf; @@ -853,148 +684,372 @@ static int hdcp2p2_txmtr_deinit(struct hdcp2p2_handle *handle) (rsp_buf->commandid != HDCP_TXMTR_DEINIT)) { pr_err("qseecom cmd failed with err = %d status = %d\n", rc, rsp_buf->status); - rc = -1; + rc = -EINVAL; goto exit; } handle->hdcp_state &= ~HDCP_STATE_TXMTR_INIT; - pr_debug("hdcp txmtr successfully deinitialized\n"); + pr_debug("success\n"); exit: return rc; } -static bool hdcp2p2_client_feature_supported(void *phdcpcontext) +static int hdcp_lib_query_stream_type(void *phdcpcontext) { int rc = 0; - bool supported = false; - struct hdcp2p2_handle *handle = phdcpcontext; - - if (!handle) { - pr_err("invalid input\n"); - goto end; - } - - rc = hdcp2p2_library_load(handle); - if (!rc) { - pr_debug("HDCP2p2 supported\n"); - hdcp2p2_library_unload(handle); - supported = true; - } -end: - return supported; -} - -static int hdcp2p2_client_start(void *phdcpcontext) -{ - int rc = 0; - struct hdcp2p2_handle *handle = phdcpcontext; + struct hdcp_query_stream_type_req *req_buf; + struct hdcp_query_stream_type_rsp *rsp_buf; + struct hdcp_lib_handle *handle = phdcpcontext; if (!handle) { pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&handle->hdcp_lock); + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_debug("hdcp library not loaded\n"); + goto exit; + } - handle->no_stored_km_flag = 0; - handle->ske_flag = 0; - handle->repeater_flag = 0; - handle->ref_cnt = 0; - handle->hdcp_state = HDCP_STATE_INIT; + if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { + pr_err("txmtr is not initialized\n"); + rc = -EINVAL; + goto exit; + } - rc = hdcp2p2_library_load(handle); - if (rc) - pr_err("error loading library\n"); + flush_kthread_worker(&handle->worker); + + /* send command to TZ */ + req_buf = (struct hdcp_query_stream_type_req *)handle-> + qseecom_handle->sbuf; + req_buf->commandid = HDCP_TXMTR_QUERY_STREAM_TYPE; + req_buf->ctxhandle = handle->tz_ctxhandle; + rsp_buf = (struct hdcp_query_stream_type_rsp *)(handle-> + qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( + struct hdcp_query_stream_type_req))); + + rc = qseecom_send_command(handle->qseecom_handle, req_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_req)), rsp_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_rsp))); + + if ((rc < 0) || (rsp_buf->status < 0) || (rsp_buf->msglen <= 0) || + (rsp_buf->commandid != HDCP_TXMTR_QUERY_STREAM_TYPE) || + (rsp_buf->msg == NULL)) { + pr_err("qseecom cmd failed with err=%d status=%d\n", + rc, rsp_buf->status); + rc = -EINVAL; + goto exit; + } + + pr_debug("message received is %s\n", + hdcp_lib_message_name((int)rsp_buf->msg[0])); + + memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); + memcpy(handle->listener_buf, (unsigned char *)rsp_buf->msg, + rsp_buf->msglen); + handle->hdcp_timeout = rsp_buf->timeout; + handle->msglen = rsp_buf->msglen; + + if (!atomic_read(&handle->hdcp_off_pending)) + hdcp_lib_send_message(handle); else - rc = hdcp2p2_txmtr_init(handle); - - mutex_unlock(&handle->hdcp_lock); + goto exit; + pr_debug("success\n"); +exit: return rc; } -static int hdcp2p2_client_end(struct hdcp2p2_handle *handle) +static bool hdcp_lib_client_feature_supported(void *phdcpcontext) { + int rc = 0; + bool supported = false; + struct hdcp_lib_handle *handle = phdcpcontext; + + if (!handle) { + pr_err("invalid input\n"); + goto exit; + } + + rc = hdcp_lib_library_load(handle); + if (!rc) { + pr_debug("HDCP2p2 supported\n"); + hdcp_lib_library_unload(handle); + supported = true; + } +exit: + return supported; +} + +static int hdcp_lib_wakeup(void *phdcpcontext, + enum hdcp_lib_wakeup_cmd cmd, char *msg, uint32_t msglen) +{ + struct hdcp_lib_handle *handle = phdcpcontext; + int rc = 0; + if (!handle) return -EINVAL; mutex_lock(&handle->hdcp_lock); + handle->wakeup_cmd = cmd; + mutex_unlock(&handle->hdcp_lock); - hdcp2p2_txmtr_deinit(handle); - hdcp2p2_library_unload(handle); + if (handle->wakeup_cmd == HDCP_WKUP_CMD_STOP) { + atomic_set(&handle->hdcp_off_pending, 1); + flush_kthread_worker(&handle->worker); + atomic_set(&handle->hdcp_off_pending, 0); + } + + mutex_lock(&handle->hdcp_lock); + + pr_debug("wakeup_cmd: %s\n", hdcp_cmd_to_str(handle->wakeup_cmd)); + + if (msglen) { + handle->last_msg_recvd_len = msglen; + + handle->last_msg_recvd_buf = kzalloc(msglen, GFP_KERNEL); + if (!handle->last_msg_recvd_buf) { + rc = -ENOMEM; + goto exit; + } + + memcpy(handle->last_msg_recvd_buf, msg, msglen); + } + + switch (handle->wakeup_cmd) { + case HDCP_WKUP_CMD_START: + handle->no_stored_km_flag = 0; + handle->repeater_flag = 0; + handle->hdcp_state = HDCP_STATE_INIT; + + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->init); + break; + case HDCP_WKUP_CMD_STOP: + /* wait for any work being executed */ + queue_kthread_work(&handle->worker, &handle->clean); + break; + case HDCP_WKUP_CMD_MSG_SEND_SUCCESS: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->msg_sent); + break; + case HDCP_WKUP_CMD_MSG_SEND_FAILED: + case HDCP_WKUP_CMD_MSG_RECV_FAILED: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + break; + case HDCP_WKUP_CMD_MSG_RECV_SUCCESS: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->msg_recvd); + break; + case HDCP_WKUP_CMD_MSG_RECV_TIMEOUT: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->timeout); + break; + default: + pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); + } +exit: + mutex_unlock(&handle->hdcp_lock); + return 0; +} + +static void hdcp_lib_msg_sent_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, msg_sent); + + if (!handle) { + pr_err("invalid handle\n"); + return; + } + + mutex_lock(&handle->hdcp_lock); + + if (handle->wakeup_cmd == HDCP_WKUP_CMD_MSG_SEND_SUCCESS) { + if (handle->last_msg_sent == SKE_SEND_EKS_MESSAGE_ID) { + if (!hdcp_lib_enable_encryption(handle)) + handle->client_ops->wakeup( + handle->client_ctx, + HDMI_HDCP_STATUS_SUCCESS, + 0, 0, 0); + } else { + hdcp_lib_recv_message(handle); + } + } else { + pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); + } mutex_unlock(&handle->hdcp_lock); - return 0; + return; } -static int hdcp2p2_client_stop(void *phdcpcontext) +static void hdcp_lib_init_work(struct kthread_work *work) { - struct hdcp2p2_handle *handle = phdcpcontext; + int rc = 0; + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, init); - if (!handle) - return -EINVAL; + if (!handle) { + pr_err("invalid handle\n"); + return; + } - atomic_set(&handle->hdcp_off, 1); + mutex_lock(&handle->hdcp_lock); - flush_work(&handle->auth_work); + if (handle->wakeup_cmd == HDCP_WKUP_CMD_START) { + rc = hdcp_lib_library_load(handle); + if (rc) + goto exit; - hdcp2p2_client_end(handle); + rc = hdcp_lib_txmtr_init(handle); + if (rc) + goto exit; - atomic_set(&handle->hdcp_off, 0); + rc = hdcp_lib_send_message(handle); + if (rc) + goto exit; + } else if (handle->wakeup_cmd == HDCP_WKUP_CMD_STOP) { + rc = hdcp_lib_txmtr_deinit(handle); + if (rc) + goto exit; - return 0; + rc = hdcp_lib_library_unload(handle); + if (rc) + goto exit; + } else { + pr_err("invalid wakeup cmd: %d\n", handle->wakeup_cmd); + } +exit: + if (rc && !atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + + mutex_unlock(&handle->hdcp_lock); + return; } -static void hdcp2p2_cleanup(void *phdcpcontext) +static void hdcp_lib_manage_timeout_work(struct kthread_work *work) { - struct hdcp2p2_handle *handle = phdcpcontext; + int rc = 0; + struct hdcp_send_timeout_req *req_buf; + struct hdcp_send_timeout_rsp *rsp_buf; + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, timeout); + + if (!handle) { + pr_err("invalid handle\n"); + return; + } + + mutex_lock(&handle->hdcp_lock); + + req_buf = (struct hdcp_send_timeout_req *) + (handle->qseecom_handle->sbuf); + req_buf->commandid = HDCP_TXMTR_SEND_MESSAGE_TIMEOUT; + req_buf->ctxhandle = handle->tz_ctxhandle; + + rsp_buf = (struct hdcp_send_timeout_rsp *)(handle-> + qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( + struct hdcp_send_timeout_req))); + + rc = qseecom_send_command(handle->qseecom_handle, req_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_req)), rsp_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_rsp))); + + if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS)) { + pr_err("qseecom cmd failed for with err = %d status = %d\n", + rc, rsp_buf->status); + rc = -EINVAL; + goto error; + } + + if (rsp_buf->commandid == HDCP_TXMTR_SEND_MESSAGE_TIMEOUT) { + pr_err("HDCP_TXMTR_SEND_MESSAGE_TIMEOUT\n"); + rc = -EINVAL; + goto error; + } + + /* + * if the response contains LC_Init message + * send the message again to TZ + */ + if ((rsp_buf->commandid == HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) && + ((int)rsp_buf->message[0] == LC_INIT_MESSAGE_ID) && + (rsp_buf->msglen == LC_INIT_MESSAGE_SIZE)) { + if (!atomic_read(&handle->hdcp_off_pending)) { + /* keep local copy of TZ response */ + memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); + memcpy(handle->listener_buf, + (unsigned char *)rsp_buf->message, + rsp_buf->msglen); + handle->hdcp_timeout = rsp_buf->timeout; + handle->msglen = rsp_buf->msglen; + + hdcp_lib_send_message(handle); + } + } +error: + if (rc && !atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + + mutex_unlock(&handle->hdcp_lock); +} + +static void hdcp_lib_cleanup_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, clean); if (!handle) { pr_err("invalid input\n"); return; }; - if (atomic_read(&handle->hdcp_off)) { + mutex_lock(&handle->hdcp_lock); + + if (atomic_read(&handle->hdcp_off_pending)) { pr_err("hdcp off underway\n"); - return; - } - - hdcp2p2_client_end(handle); - - /* notify client so that client can cleanup */ - handle->client_ops->hdcp_tz_error(handle->client_ctx); -} - -static int hdcp2p2_txmtr_process_message(void *phdcpcontext, - unsigned char *msg, uint32_t msglen) -{ - int rc = 0; - struct hdcp2p2_handle *handle = phdcpcontext; - struct hdcp_rcvd_msg_req *req_buf; - struct hdcp_rcvd_msg_rsp *rsp_buf; - - if (!handle) { - pr_err("invalid input\n"); - rc = -EINVAL; goto exit; } - if ((!msg) || (msglen <= 0)) { + hdcp_lib_txmtr_deinit(handle); + hdcp_lib_library_unload(handle); + + if (handle->wakeup_cmd != HDCP_WKUP_CMD_STOP) + hdcp_lib_report_error(handle); +exit: + mutex_unlock(&handle->hdcp_lock); +} + +static void hdcp_lib_msg_recvd_work(struct kthread_work *work) +{ + int rc = 0; + struct hdcp_rcvd_msg_req *req_buf; + struct hdcp_rcvd_msg_rsp *rsp_buf; + uint32_t msglen; + char *msg; + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, msg_recvd); + + if (!handle) { + pr_err("invalid handle\n"); + return; + } + + mutex_lock(&handle->hdcp_lock); + + msg = handle->last_msg_recvd_buf; + msglen = handle->last_msg_recvd_len; + + if (msglen <= 0) { pr_err("invalid msg\n"); rc = -EINVAL; goto exit; } - if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { - pr_err("hdcp library is not loaded\n"); - goto exit; - } - - if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { - pr_err("txmtr not initialized\n"); - goto exit; - } + pr_debug("msg received: %s from sink\n", + hdcp_lib_message_name((int)msg[0])); /* send the message to QSEECOM */ req_buf = (struct hdcp_rcvd_msg_req *)(handle-> @@ -1008,14 +1063,18 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( struct hdcp_rcvd_msg_req))); + pr_debug("writing %s to TZ at %dms\n", + hdcp_lib_message_name((int)msg[0]), + jiffies_to_msecs(jiffies)); + rc = qseecom_send_command(handle->qseecom_handle, req_buf, QSEECOM_ALIGN(sizeof(struct hdcp_rcvd_msg_req)), rsp_buf, QSEECOM_ALIGN(sizeof(struct hdcp_rcvd_msg_rsp))); - /* No response was obtained from TZ */ + /* get next message from sink if we receive H PRIME on no store km */ if ((msg[0] == AKE_SEND_H_PRIME_MESSAGE_ID) && handle->no_stored_km_flag) { - pr_debug("Got HPrime from tx, nothing sent to rx\n"); + hdcp_lib_recv_message(handle); goto exit; } @@ -1030,12 +1089,13 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, (rsp_buf->msg == NULL)) { pr_err("qseecom cmd failed with err=%d status=%d\n", rc, rsp_buf->status); - rc = -1; + rc = -EINVAL; goto exit; } - pr_debug("message received is %s\n", - hdcp2p2_message_name((int)rsp_buf->msg[0])); + pr_debug("recvd %s from TZ at %dms\n", + hdcp_lib_message_name((int)rsp_buf->msg[0]), + jiffies_to_msecs(jiffies)); /* set the flag if response is AKE_No_Stored_km */ if (((int)rsp_buf->msg[0] == AKE_NO_STORED_KM_MESSAGE_ID)) { @@ -1045,15 +1105,9 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, handle->no_stored_km_flag = 0; } - /* - * set ske flag is response is SKE_SEND_EKS - * also set repeater flag if it's a repeater - */ - handle->ske_flag = 0; + /* check if it's a repeater */ if ((rsp_buf->msg[0] == SKE_SEND_EKS_MESSAGE_ID) && (rsp_buf->msglen == SKE_SEND_EKS_MESSAGE_SIZE)) { - handle->ske_flag = 1; - /* check if it's a repeater */ if ((rsp_buf->flag == HDCP_TXMTR_SUBSTATE_WAITING_FOR_RECIEVERID_LIST) && (rsp_buf->timeout > 0)) @@ -1066,15 +1120,15 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, handle->hdcp_timeout = rsp_buf->timeout; handle->msglen = rsp_buf->msglen; - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - - pr_debug("hdcp txmtr process message success\n"); - + if (!atomic_read(&handle->hdcp_off_pending)) + hdcp_lib_send_message(handle); exit: - return rc; + kzfree(handle->last_msg_recvd_buf); + + if (rc && !atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + + mutex_unlock(&handle->hdcp_lock); } /* APIs exposed to all clients */ @@ -1136,7 +1190,7 @@ int hdcp_library_register(void **pphdcpcontext, void *client_ctx) { int rc = 0; - struct hdcp2p2_handle *handle = NULL; + struct hdcp_lib_handle *handle = NULL; if (!pphdcpcontext) { pr_err("invalid input: context passed\n"); @@ -1154,12 +1208,9 @@ int hdcp_library_register(void **pphdcpcontext, } /* populate ops to be called by client */ - txmtr_ops->start = hdcp2p2_client_start; - txmtr_ops->stop = hdcp2p2_client_stop; - txmtr_ops->feature_supported = hdcp2p2_client_feature_supported; - txmtr_ops->process_message = hdcp2p2_txmtr_process_message; - txmtr_ops->hdcp_txmtr_query_stream_type = - hdcp2p2_txmtr_query_stream_type; + txmtr_ops->feature_supported = hdcp_lib_client_feature_supported; + txmtr_ops->wakeup = hdcp_lib_wakeup; + txmtr_ops->hdcp_query_stream_type = hdcp_lib_query_stream_type; handle = kzalloc(sizeof(*handle), GFP_KERNEL); if (!handle) { @@ -1170,16 +1221,17 @@ int hdcp_library_register(void **pphdcpcontext, handle->client_ctx = client_ctx; handle->client_ops = client_ops; + atomic_set(&handle->hdcp_off_pending, 0); + mutex_init(&handle->hdcp_lock); - handle->hdcp_workqueue = create_singlethread_workqueue("hdcp-module"); - if (handle->hdcp_workqueue == NULL) { - pr_err("hdcp wq creation failed\n"); - rc = -EFAULT; - goto error; - } + init_kthread_worker(&handle->worker); - INIT_WORK(&handle->auth_work, hdcp2p2_authenticate_work); + init_kthread_work(&handle->init, hdcp_lib_init_work); + init_kthread_work(&handle->msg_sent, hdcp_lib_msg_sent_work); + init_kthread_work(&handle->msg_recvd, hdcp_lib_msg_recvd_work); + init_kthread_work(&handle->timeout, hdcp_lib_manage_timeout_work); + init_kthread_work(&handle->clean, hdcp_lib_cleanup_work); handle->listener_buf = kzalloc(MAX_TX_MESSAGE_SIZE, GFP_KERNEL); if (!(handle->listener_buf)) { @@ -1187,18 +1239,20 @@ int hdcp_library_register(void **pphdcpcontext, goto error; } - *((struct hdcp2p2_handle **)pphdcpcontext) = handle; + *((struct hdcp_lib_handle **)pphdcpcontext) = handle; - pr_debug("hdcp lib successfully registered\n"); + handle->thread = kthread_run(kthread_worker_fn, + &handle->worker, "hdcp_tz_lib"); + + if (IS_ERR(handle->thread)) { + pr_err("unable to start lib thread\n"); + rc = PTR_ERR(handle->thread); + handle->thread = NULL; + goto error; + } return 0; error: - /* deallocate resources */ - if (handle->hdcp_workqueue != NULL) { - destroy_workqueue(handle->hdcp_workqueue); - handle->hdcp_workqueue = NULL; - } - kzfree(handle->listener_buf); handle->listener_buf = NULL; kzfree(handle); @@ -1209,15 +1263,14 @@ unlock: void hdcp_library_deregister(void *phdcpcontext) { - struct hdcp2p2_handle *handle = phdcpcontext; + struct hdcp_lib_handle *handle = phdcpcontext; if (!handle) return; - kzfree(handle->qseecom_handle); + kthread_stop(handle->thread); - if (handle->hdcp_workqueue) - destroy_workqueue(handle->hdcp_workqueue); + kzfree(handle->qseecom_handle); mutex_destroy(&handle->hdcp_lock); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index 060adcf5d319..8d4ad007efba 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -10,18 +10,20 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include #include #include #include #include +#include #include #include "mdss_hdmi_hdcp.h" #include "video/msm_hdmi_hdcp_mgr.h" #include "mdss_hdmi_util.h" - /* * Defined addresses and offsets of standard HDCP 2.2 sink registers * for DDC, as defined in HDCP 2.2 spec section 2.14 table 2.7 @@ -38,237 +40,197 @@ */ #define MIN_HDMI_TX_MAJOR_VERSION 4 -struct hdcp2p2_message_map { - int msg_id; - const char *msg_name; -}; - -enum hdcp2p2_sink_status { +enum hdmi_hdcp2p2_sink_status { SINK_DISCONNECTED, SINK_CONNECTED }; -enum hdcp2p2_sink_originated_message { - START_AUTHENTICATION = 0, - AKE_SEND_CERT_MESSAGE = 3, - AKE_SEND_H_PRIME_MESSAGE = 7, - AKE_SEND_PAIRING_INFO_MESSAGE = 8, - LC_SEND_L_PRIME_MESSAGE = 10, -}; - -enum hdcp2p2_tx_originated_message { - AKE_INIT_MESSAGE = 2, - AKE_NO_STORED_KM_MESSAGE = 4, - AKE_STORED_KM_MESSAGE = 5, - LC_INIT_MESSAGE = 9, - SKE_SEND_EKS_MESSAGE = 11, -}; - -/* Stores one single message from sink */ -struct hdcp2p2_message { - u8 *message_bytes; /* Message buffer */ - size_t msg_size; /* Byte size of the message buffer */ -}; - -struct hdcp2p2_ctrl { - enum hdmi_hdcp_state auth_state; /* Current auth message st */ - enum hdcp2p2_sink_status sink_status; /* Is sink connected */ +struct hdmi_hdcp2p2_ctrl { + atomic_t auth_state; + enum hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */ struct hdmi_hdcp_init_data init_data; /* Feature data from HDMI drv */ - enum hdcp2p2_sink_originated_message next_sink_message; - enum hdcp2p2_tx_originated_message next_tx_message; - bool tx_has_master_key; /* true when TX has a stored Km for sink */ - struct mutex mutex; /* mutex to protect access to hdcp2p2_ctrl */ + struct mutex mutex; /* mutex to protect access to ctrl */ struct hdmi_hdcp_ops *ops; - void *hdcp_lib_handle; /* Handle to HDCP 2.2 Trustzone library */ + void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */ struct hdcp_txmtr_ops *txmtr_ops; /* Ops for driver to call into TZ */ struct hdcp_client_ops *client_ops; /* Ops for driver to export to TZ */ struct completion rxstatus_completion; /* Rx status interrupt */ + + enum hdmi_hdcp_cmd wakeup_cmd; + char *send_msg_buf; + uint32_t send_msg_len; + uint32_t timeout; + + struct task_struct *thread; + struct kthread_worker worker; + struct kthread_work status; + struct kthread_work auth; + struct kthread_work send_msg; + struct kthread_work recv_msg; }; -static int hdcp2p2_authenticate(void *input); - -static const char *hdcp2p2_message_name(int msg_id) +static void hdmi_hdcp2p2_reset(struct hdmi_hdcp2p2_ctrl *ctrl) { - /* - * Message ID map. The first number indicates the message number - * assigned to the message by the HDCP 2.2 spec. This is also the first - * byte of every HDCP 2.2 authentication protocol message. - */ - static struct hdcp2p2_message_map hdcp2p2_msg_map[] = { - {2, "AKE_INIT"}, - {3, "AKE_SEND_CERT"}, - {4, "AKE_NO_STORED_KM"}, - {5, "AKE_STORED_KM"}, - {7, "AKE_SEND_H_PRIME"}, - {8, "AKE_SEND_PAIRING_INFO"}, - {9, "LC_INIT"}, - {10, "LC_SEND_L_PRIME"}, - {11, "SKE_SEND_EKS"}, - {12, "REPEATER_AUTH_SEND_RECEIVERID_LIST"}, - {15, "REPEATER_AUTH_SEND_ACK"}, - {16, "REPEATER_AUTH_STREAM_MANAGE"}, - {17, "REPEATER_AUTH_STREAM_READY"}, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(hdcp2p2_msg_map); i++) { - if (msg_id == hdcp2p2_msg_map[i].msg_id) - return hdcp2p2_msg_map[i].msg_name; + if (!ctrl) { + pr_err("invalid input\n"); + return; } - return "UNKNOWN"; + + ctrl->sink_status = SINK_DISCONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); } -static ssize_t hdcp2p2_sysfs_rda_sink_status(struct device *dev, - struct device_attribute *attr, char *buf) +static void hdmi_hdcp2p2_off(void *input) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = - hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - ssize_t ret; + struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + hdmi_hdcp2p2_reset(ctrl); + + flush_kthread_worker(&ctrl->worker); + + mutex_lock(&ctrl->mutex); + queue_kthread_work(&ctrl->worker, &ctrl->auth); + mutex_unlock(&ctrl->mutex); +} + +static int hdmi_hdcp2p2_authenticate(void *input) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = input; + int rc = 0; + + flush_kthread_worker(&ctrl->worker); + + mutex_lock(&ctrl->mutex); + ctrl->sink_status = SINK_CONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING); + + queue_kthread_work(&ctrl->worker, &ctrl->auth); + + mutex_unlock(&ctrl->mutex); + return rc; +} + +static int hdmi_hdcp2p2_reauthenticate(void *input) +{ + int rc = 0; + struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; + + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&hdcp2p2_ctrl->mutex); - if (hdcp2p2_ctrl->sink_status == SINK_CONNECTED) + hdmi_hdcp2p2_reset((struct hdmi_hdcp2p2_ctrl *)input); + + rc = hdmi_hdcp2p2_authenticate(input); + + return rc; +} + +static ssize_t hdmi_hdcp2p2_sysfs_rda_sink_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = + hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); + ssize_t ret; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + mutex_lock(&ctrl->mutex); + if (ctrl->sink_status == SINK_CONNECTED) ret = scnprintf(buf, PAGE_SIZE, "Connected\n"); else ret = scnprintf(buf, PAGE_SIZE, "Disconnected\n"); - mutex_unlock(&hdcp2p2_ctrl->mutex); + mutex_unlock(&ctrl->mutex); return ret; } -static ssize_t hdcp2p2_sysfs_rda_trigger(struct device *dev, +static ssize_t hdmi_hdcp2p2_sysfs_rda_trigger(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; - struct hdcp2p2_ctrl *hdcp2p2_ctrl = + struct hdmi_hdcp2p2_ctrl *ctrl = hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&hdcp2p2_ctrl->mutex); - if (hdcp2p2_ctrl->sink_status == SINK_CONNECTED) + mutex_lock(&ctrl->mutex); + if (ctrl->sink_status == SINK_CONNECTED) ret = scnprintf(buf, PAGE_SIZE, "Triggered\n"); else ret = scnprintf(buf, PAGE_SIZE, "Not triggered\n"); - mutex_unlock(&hdcp2p2_ctrl->mutex); + mutex_unlock(&ctrl->mutex); return ret; } -static ssize_t hdcp2p2_sysfs_wta_trigger(struct device *dev, +static ssize_t hdmi_hdcp2p2_sysfs_wta_trigger(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = + struct hdmi_hdcp2p2_ctrl *ctrl = hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&hdcp2p2_ctrl->mutex); - hdcp2p2_ctrl->sink_status = SINK_CONNECTED; - mutex_unlock(&hdcp2p2_ctrl->mutex); + mutex_lock(&ctrl->mutex); + ctrl->sink_status = SINK_CONNECTED; + mutex_unlock(&ctrl->mutex); - DEV_DBG("%s: HDCP 2.2 authentication triggered\n", __func__); - hdcp2p2_authenticate(hdcp2p2_ctrl); + pr_debug("HDCP 2.2 authentication triggered\n"); + hdmi_hdcp2p2_authenticate(ctrl); return count; } -static ssize_t hdcp2p2_sysfs_wta_min_level_change(struct device *dev, +static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = + struct hdmi_hdcp2p2_ctrl *ctrl = hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); int res; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - DEV_DBG("%s: notification of minimum level change received\n", - __func__); - res = hdcp2p2_ctrl->txmtr_ops-> - hdcp_txmtr_query_stream_type(hdcp2p2_ctrl->hdcp_lib_handle); + pr_debug("notification of minimum level change received\n"); + res = ctrl->txmtr_ops-> + hdcp_query_stream_type(ctrl->lib_ctx); return count; } -static void hdcp2p2_auth_failed(struct hdcp2p2_ctrl *hdcp2p2_ctrl) +static void hdmi_hdcp2p2_auth_failed(struct hdmi_hdcp2p2_ctrl *ctrl) { - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return; } - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->tx_has_master_key = false; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); /* notify hdmi tx about HDCP failure */ - hdcp2p2_ctrl->init_data.notify_status( - hdcp2p2_ctrl->init_data.cb_data, + ctrl->init_data.notify_status( + ctrl->init_data.cb_data, HDCP_STATE_AUTH_FAIL); } -static void hdcp2p2_advance_next_tx_message( - struct hdcp2p2_ctrl *hdcp2p2_ctrl) -{ - switch (hdcp2p2_ctrl->next_tx_message) { - case AKE_INIT_MESSAGE: - hdcp2p2_ctrl->next_tx_message = - AKE_NO_STORED_KM_MESSAGE; - break; - case AKE_NO_STORED_KM_MESSAGE: - case AKE_STORED_KM_MESSAGE: - hdcp2p2_ctrl->next_tx_message = LC_INIT_MESSAGE; - break; - case LC_INIT_MESSAGE: - hdcp2p2_ctrl->next_tx_message = SKE_SEND_EKS_MESSAGE; - break; - default: - break; - } -} - -static void hdcp2p2_advance_next_sink_message( - struct hdcp2p2_ctrl *hdcp2p2_ctrl) -{ - switch (hdcp2p2_ctrl->next_sink_message) { - case START_AUTHENTICATION: - hdcp2p2_ctrl->next_sink_message = AKE_SEND_CERT_MESSAGE; - break; - case AKE_SEND_CERT_MESSAGE: - hdcp2p2_ctrl->next_sink_message = - AKE_SEND_H_PRIME_MESSAGE; - break; - case AKE_SEND_H_PRIME_MESSAGE: - if (hdcp2p2_ctrl->tx_has_master_key) - hdcp2p2_ctrl->next_sink_message = - LC_SEND_L_PRIME_MESSAGE; - else - hdcp2p2_ctrl->next_sink_message = - AKE_SEND_PAIRING_INFO_MESSAGE; - break; - case AKE_SEND_PAIRING_INFO_MESSAGE: - hdcp2p2_ctrl->next_sink_message = - LC_SEND_L_PRIME_MESSAGE; - break; - default: - break; - } -} - -static int hdcp2p2_ddc_read_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, - int size, u32 timeout) +static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl, + u8 *buf, int size, u32 timeout) { struct hdmi_tx_ddc_data ddc_data; int rc; @@ -283,14 +245,14 @@ static int hdcp2p2_ddc_read_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, ddc_data.hard_timeout = timeout; ddc_data.what = "HDCP2ReadMessage"; - rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data); + rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl, &ddc_data); if (rc) - DEV_ERR("%s: Cannot read HDCP message register", __func__); + pr_err("Cannot read HDCP message register\n"); return rc; } -static int hdcp2p2_ddc_write_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, - size_t size) +static int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl, + u8 *buf, size_t size) { struct hdmi_tx_ddc_data ddc_data; int rc; @@ -303,13 +265,13 @@ static int hdcp2p2_ddc_write_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, ddc_data.retry = 1; ddc_data.what = "HDCP2WriteMessage"; - rc = hdmi_ddc_write(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data); + rc = hdmi_ddc_write(ctrl->init_data.ddc_ctrl, &ddc_data); if (rc) - DEV_ERR("%s: Cannot write HDCP message register", __func__); + pr_err("Cannot write HDCP message register"); return rc; } -static void hdcp2p2_ddc_abort(struct hdcp2p2_ctrl *hdcp2p2_ctrl) +static void hdmi_hdcp2p2_ddc_abort(struct hdmi_hdcp2p2_ctrl *ctrl) { /* Abort any ongoing DDC transactions */ struct hdmi_tx_ddc_data ddc_data; @@ -317,11 +279,11 @@ static void hdcp2p2_ddc_abort(struct hdcp2p2_ctrl *hdcp2p2_ctrl) memset(&ddc_data, 0, sizeof(ddc_data)); ddc_data.retry = 1; ddc_data.what = "HDCPAbortTransaction"; - hdmi_ddc_abort_transaction(hdcp2p2_ctrl->init_data.ddc_ctrl, + hdmi_ddc_abort_transaction(ctrl->init_data.ddc_ctrl, &ddc_data); } -static int hdcp2p2_read_version(struct hdcp2p2_ctrl *hdcp2p2_ctrl, +static int hdmi_hdcp2p2_read_version(struct hdmi_hdcp2p2_ctrl *ctrl, u8 *hdcp2version) { struct hdmi_tx_ddc_data ddc_data; @@ -336,95 +298,201 @@ static int hdcp2p2_read_version(struct hdcp2p2_ctrl *hdcp2p2_ctrl, ddc_data.retry = 1; ddc_data.what = "HDCP2Version"; - rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data); + rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl, &ddc_data); if (rc) { - DEV_ERR("%s: Cannot read HDCP2Version register", __func__); + pr_err("Cannot read HDCP2Version register"); return rc; } - DEV_DBG("%s: Read HDCP2Version as %u\n", __func__, *hdcp2version); + pr_debug("Read HDCP2Version as %u\n", *hdcp2version); return rc; } -static int hdcp2p2_read_message_from_sink(struct hdcp2p2_ctrl *hdcp2p2_ctrl, - int msg_size, u32 timeout) +static ssize_t hdmi_hdcp2p2_sysfs_rda_hdcp2_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 hdcp2version; + ssize_t ret; + struct hdmi_hdcp2p2_ctrl *ctrl = + hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + ret = hdmi_hdcp2p2_read_version(ctrl, &hdcp2version); + if (ret < 0) + return ret; + return snprintf(buf, PAGE_SIZE, "%u\n", hdcp2version); +} + + +static DEVICE_ATTR(trigger, S_IRUGO | S_IWUSR, hdmi_hdcp2p2_sysfs_rda_trigger, + hdmi_hdcp2p2_sysfs_wta_trigger); +static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, + hdmi_hdcp2p2_sysfs_wta_min_level_change); +static DEVICE_ATTR(sink_status, S_IRUGO, hdmi_hdcp2p2_sysfs_rda_sink_status, + NULL); +static DEVICE_ATTR(hdcp2_version, S_IRUGO, hdmi_hdcp2p2_sysfs_rda_hdcp2_version, + NULL); + +static struct attribute *hdmi_hdcp2p2_fs_attrs[] = { + &dev_attr_trigger.attr, + &dev_attr_min_level_change.attr, + &dev_attr_sink_status.attr, + &dev_attr_hdcp2_version.attr, + NULL, +}; + +static struct attribute_group hdmi_hdcp2p2_fs_attr_group = { + .name = "hdcp2p2", + .attrs = hdmi_hdcp2p2_fs_attrs, +}; + +static int hdmi_hdcp2p2_isr(void *input) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = input; + u32 reg_val; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + pr_debug("INT_CTRL0 is 0x%x\n", + DSS_REG_R(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0)); + reg_val = DSS_REG_R(ctrl->init_data.core_io, + HDMI_HDCP_INT_CTRL2); + if (reg_val & BIT(0)) { + pr_debug("HDCP 2.2 Encryption is enabled\n"); + reg_val |= BIT(1); + DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, + reg_val); + } + + reg_val = DSS_REG_R(ctrl->init_data.core_io, + HDMI_DDC_INT_CTRL0); + if (reg_val & HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK) { + DSS_REG_W(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, + reg_val & ~(BIT(31))); + if (!completion_done(&ctrl->rxstatus_completion)) + complete_all(&ctrl->rxstatus_completion); + } else if (reg_val & BIT(8)) { + DSS_REG_W(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, + reg_val & ~(BIT(9) | BIT(10))); + if (!completion_done(&ctrl->rxstatus_completion)) + complete_all(&ctrl->rxstatus_completion); + } + + return 0; +} + +static bool hdmi_hdcp2p2_feature_supported(void *input) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = input; + struct hdcp_txmtr_ops *lib = NULL; + bool supported = false; + + if (!ctrl) { + pr_err("invalid input\n"); + goto end; + } + + lib = ctrl->txmtr_ops; + if (!lib) { + pr_err("invalid lib ops data\n"); + goto end; + } + + if (lib->feature_supported) + supported = lib->feature_supported( + ctrl->lib_ctx); +end: + return supported; +} + +static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) { - bool read_next_message = false; int rc = 0; - struct hdcp2p2_message *message = kmalloc(sizeof(*message), - GFP_KERNEL); + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, send_msg); + struct hdcp_txmtr_ops *lib = NULL; + enum hdcp_lib_wakeup_cmd cmd; - if (!message) { - DEV_ERR("%s: Could not allocate memory\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + mutex_lock(&ctrl->mutex); + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); goto exit; } - message->message_bytes = kmalloc(msg_size, GFP_KERNEL); - if (!message->message_bytes) - goto exit; + lib = ctrl->txmtr_ops; - DEV_DBG("%s: reading message from sink\n", __func__); - rc = hdcp2p2_ddc_read_message(hdcp2p2_ctrl, message->message_bytes, - msg_size, timeout); + if (!lib) { + pr_err("invalid lib data\n"); + goto exit; + } + + /* Forward the message to the sink */ + rc = hdmi_hdcp2p2_ddc_write_message(ctrl, + ctrl->send_msg_buf, + (size_t)ctrl->send_msg_len); if (rc) { - DEV_ERR("%s: ERROR reading message from sink\n", __func__); - goto exit; + pr_err("Error sending msg to sink %d\n", rc); + cmd = HDCP_WKUP_CMD_MSG_SEND_FAILED; + } else { + cmd = HDCP_WKUP_CMD_MSG_SEND_SUCCESS; } - message->msg_size = msg_size; - DEV_DBG("%s: msg recvd: %s, size: %d\n", __func__, - hdcp2p2_message_name((int)message->message_bytes[0]), - msg_size); + if (lib->wakeup) + lib->wakeup(ctrl->lib_ctx, cmd, 0, 0); +exit: + kzfree(ctrl->send_msg_buf); + mutex_unlock(&ctrl->mutex); +} - if (hdcp2p2_ctrl->txmtr_ops->process_message) { - rc = hdcp2p2_ctrl->txmtr_ops->process_message( - hdcp2p2_ctrl->hdcp_lib_handle, message->message_bytes, - (u32)message->msg_size); - if (rc) - goto exit; - } else { - DEV_ERR("%s: process message not defined\n", __func__); +static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) +{ + int rc = 0, msg_size = 0, retries = 5; + u64 mult; + char *recvd_msg_buf = NULL; + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, recv_msg); + struct hdcp_txmtr_ops *lib = NULL; + enum hdcp_lib_wakeup_cmd cmd; + struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data; + struct hdmi_tx_ddc_ctrl *ddc_ctrl; + struct msm_hdmi_mode_timing_info *timing; + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + mutex_lock(&ctrl->mutex); + + lib = ctrl->txmtr_ops; + + if (!lib) { + pr_err("invalid lib data\n"); rc = -EINVAL; goto exit; } - if (hdcp2p2_ctrl->next_sink_message == AKE_SEND_H_PRIME_MESSAGE) { - if (!hdcp2p2_ctrl->tx_has_master_key) { - DEV_DBG("%s: Listening for second message\n", __func__); - hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl); - read_next_message = true; - } + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; } - if (read_next_message) - rc = 1; -exit: - kfree(message->message_bytes); - kfree(message); - - return rc; -} - -static int hdcp2p2_sink_message_read(struct hdcp2p2_ctrl *hdcp2p2_ctrl, - u32 timeout) -{ - struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data; - struct hdmi_tx_ddc_ctrl *ddc_ctrl; - int rc = 0; - int msg_size; - bool read_next_message = false; - u64 mult; - struct msm_hdmi_mode_timing_info *timing; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid hdcp2p2 data\n", __func__); - return -EINVAL; - } - - ddc_ctrl = hdcp2p2_ctrl->init_data.ddc_ctrl; + ddc_ctrl = ctrl->init_data.ddc_ctrl; do { - timing = hdcp2p2_ctrl->init_data.timing; + timing = ctrl->init_data.timing; mult = hdmi_tx_get_v_total(timing) / 20; memset(&hdcp2p2_ddc_data, 0, sizeof(hdcp2p2_ddc_data)); @@ -437,467 +505,318 @@ static int hdcp2p2_sink_message_read(struct hdcp2p2_ctrl *hdcp2p2_ctrl, hdcp2p2_ddc_data.poll_sink = false; hdmi_ddc_config(ddc_ctrl); - DEV_DBG("%s: Reading rxstatus, timer delay %u\n", - __func__, (u32)mult); + pr_debug("Reading rxstatus, timer delay %u\n", (u32)mult); rc = hdmi_hdcp2p2_ddc_read_rxstatus( ddc_ctrl, &hdcp2p2_ddc_data, - &hdcp2p2_ctrl->rxstatus_completion); + &ctrl->rxstatus_completion); if (rc) { - DEV_ERR("%s: Could not read rxstatus from sink\n", - __func__); - goto exit; - } else { - DEV_DBG("%s: SUCCESS reading rxstatus\n", __func__); + pr_err("Could not read rxstatus from sink\n"); + continue; } if (!msg_size) { - DEV_ERR("%s: recvd invalid message size\n", __func__); + pr_err("recvd invalid message size\n"); rc = -EINVAL; - goto exit; + continue; } + } while (rc && (retries-- > 0)); - rc = hdcp2p2_read_message_from_sink( - hdcp2p2_ctrl, msg_size, timeout); - if (rc > 0) { - read_next_message = true; - rc = 0; - } - - hdcp2p2_ddc_abort(hdcp2p2_ctrl); - hdmi_hdcp2p2_ddc_reset(ddc_ctrl); - hdmi_hdcp2p2_ddc_disable(ddc_ctrl); - } while (read_next_message); -exit: - return rc; -} - -static ssize_t hdcp2p2_sysfs_rda_hdcp2_version(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 hdcp2version; - ssize_t ret; - struct hdcp2p2_ctrl *hdcp2p2_ctrl = - hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; - } - ret = hdcp2p2_read_version(hdcp2p2_ctrl, &hdcp2version); - if (ret < 0) - return ret; - return snprintf(buf, PAGE_SIZE, "%u\n", hdcp2version); -} - - -static DEVICE_ATTR(trigger, S_IRUGO | S_IWUSR, hdcp2p2_sysfs_rda_trigger, - hdcp2p2_sysfs_wta_trigger); -static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, - hdcp2p2_sysfs_wta_min_level_change); -static DEVICE_ATTR(sink_status, S_IRUGO, hdcp2p2_sysfs_rda_sink_status, - NULL); -static DEVICE_ATTR(hdcp2_version, S_IRUGO, - hdcp2p2_sysfs_rda_hdcp2_version, - NULL); - - -static struct attribute *hdcp2p2_fs_attrs[] = { - &dev_attr_trigger.attr, - &dev_attr_min_level_change.attr, - &dev_attr_sink_status.attr, - &dev_attr_hdcp2_version.attr, - NULL, -}; - -static struct attribute_group hdcp2p2_fs_attr_group = { - .name = "hdcp2p2", - .attrs = hdcp2p2_fs_attrs, -}; - - -static int hdcp2p2_isr(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; - u32 reg_val; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; - } - - DEV_DBG("%s\n INT_CTRL0 is 0x%x\n", __func__, - DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0)); - reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, - HDMI_HDCP_INT_CTRL2); - if (reg_val & BIT(0)) { - DEV_DBG("%s: HDCP 2.2 Encryption is enabled\n", __func__); - reg_val |= BIT(1); - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, - reg_val); - } - - reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, - HDMI_DDC_INT_CTRL0); - if (reg_val & HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK) { - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, - reg_val & ~(BIT(31))); - if (!completion_done(&hdcp2p2_ctrl->rxstatus_completion)) - complete_all(&hdcp2p2_ctrl->rxstatus_completion); - } else if (reg_val & BIT(8)) { - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, - reg_val & ~(BIT(9) | BIT(10))); - if (!completion_done(&hdcp2p2_ctrl->rxstatus_completion)) - complete_all(&hdcp2p2_ctrl->rxstatus_completion); - } - - return 0; -} - -static bool hdcp2p2_feature_supported(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; - struct hdcp_txmtr_ops *lib = NULL; - bool supported = false; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - goto end; - } - - lib = hdcp2p2_ctrl->txmtr_ops; - if (!lib) { - DEV_ERR("%s: invalid lib ops data\n", __func__); - goto end; - } - - if (lib->feature_supported) - supported = lib->feature_supported( - hdcp2p2_ctrl->hdcp_lib_handle); -end: - return supported; -} - -static void hdcp2p2_reset(struct hdcp2p2_ctrl *hdcp2p2_ctrl) -{ - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return; - } - - hdcp2p2_ctrl->sink_status = SINK_DISCONNECTED; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->auth_state = HDCP_STATE_INACTIVE; - hdcp2p2_ctrl->tx_has_master_key = false; -} - -static int hdcp2p2_authenticate(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; - struct hdcp_txmtr_ops *lib = NULL; - int rc = 0; - - mutex_lock(&hdcp2p2_ctrl->mutex); - hdcp2p2_ctrl->sink_status = SINK_CONNECTED; - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATING; - - lib = hdcp2p2_ctrl->txmtr_ops; - if (!lib) { - DEV_ERR("%s: invalid lib ops data\n", __func__); - goto done; - } - - if (!lib->start) { - DEV_ERR("%s: lib start not defined\n", __func__); - goto done; - } - - rc = lib->start(hdcp2p2_ctrl->hdcp_lib_handle); if (rc) { - DEV_ERR("%s: lib start failed\n", __func__); - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL; - - goto done; - } -done: - mutex_unlock(&hdcp2p2_ctrl->mutex); - return rc; -} - -static int hdcp2p2_reauthenticate(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; + pr_err("error reading valid rxstatus data\n"); + goto exit; } - hdcp2p2_reset((struct hdcp2p2_ctrl *)input); + recvd_msg_buf = kzalloc(msg_size, GFP_KERNEL); + if (!recvd_msg_buf) + goto exit; - return hdcp2p2_authenticate(input); + rc = hdmi_hdcp2p2_ddc_read_message(ctrl, recvd_msg_buf, + msg_size, ctrl->timeout); + if (rc) + pr_err("ERROR reading message from sink\n"); + + hdmi_hdcp2p2_ddc_abort(ctrl); + hdmi_hdcp2p2_ddc_reset(ddc_ctrl); + hdmi_hdcp2p2_ddc_disable(ddc_ctrl); +exit: + if (rc == -ETIMEDOUT) + cmd = HDCP_WKUP_CMD_MSG_RECV_TIMEOUT; + else if (rc) + cmd = HDCP_WKUP_CMD_MSG_RECV_FAILED; + else + cmd = HDCP_WKUP_CMD_MSG_RECV_SUCCESS; + + if (lib->wakeup) + lib->wakeup(ctrl->lib_ctx, cmd, + recvd_msg_buf, msg_size); + + kfree(recvd_msg_buf); + mutex_unlock(&ctrl->mutex); } -static void hdcp2p2_off(void *input) +static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input; - struct hdcp_txmtr_ops *lib = NULL; + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, status); - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return; } - mutex_lock(&hdcp2p2_ctrl->mutex); - hdcp2p2_reset(hdcp2p2_ctrl); + mutex_lock(&ctrl->mutex); - lib = hdcp2p2_ctrl->txmtr_ops; - - if (lib && lib->stop) - lib->stop(hdcp2p2_ctrl->hdcp_lib_handle); - - mutex_unlock(&hdcp2p2_ctrl->mutex); -} - -static int hdcp2p2_send_message_to_sink(void *client_ctx, - char *message, u32 msg_size) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = client_ctx; - int rc = 0; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); goto exit; } - if (hdcp2p2_ctrl->auth_state == HDCP_STATE_INACTIVE) { - DEV_DBG("%s: hdcp is off\n", __func__); - goto exit; - } - - DEV_DBG("%s: Sending %s message from tx to sink\n", __func__, - hdcp2p2_message_name((int)message[0])); - - switch (message[0]) { - case AKE_STORED_KM_MESSAGE: - hdcp2p2_ctrl->tx_has_master_key = true; - hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl); - hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl); - break; - case SKE_SEND_EKS_MESSAGE: - /* Send EKS message comes from TX when we are authenticated */ - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATED; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->tx_has_master_key = false; - hdcp2p2_ctrl->init_data.notify_status( - hdcp2p2_ctrl->init_data.cb_data, - HDCP_STATE_AUTHENTICATED); - break; - case AKE_NO_STORED_KM_MESSAGE: - /* - * We need a delay to allow sink time to be ready to receive the - * message - */ - msleep(100); - /* fall through */ - default: - hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl); - hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl); - break; - } - - /* Forward the message to the sink */ - rc = hdcp2p2_ddc_write_message(hdcp2p2_ctrl, message, (size_t)msg_size); - if (rc) - DEV_ERR("%s: Error in writing to sink %d\n", __func__, rc); - else - DEV_DBG("%s: SUCCESS\n", __func__); -exit: - mutex_unlock(&hdcp2p2_ctrl->mutex); - - return rc; -} - -static int hdcp2p2_recv_message_from_sink(void *client_ctx, - char *message, u32 msg_size, u32 timeout) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = client_ctx; - int rc = 0; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid ctrl\n", __func__); - return -EINVAL; - } - - if (!message) { - DEV_ERR("%s: invalid message\n", __func__); - return -EINVAL; - } - - if (hdcp2p2_ctrl->auth_state == HDCP_STATE_INACTIVE) { - DEV_DBG("%s: hdcp is off\n", __func__); - return 0; - } - - /* Start polling sink for the next expected message in the protocol */ - if (message[0] != SKE_SEND_EKS_MESSAGE) { - rc = hdcp2p2_sink_message_read(hdcp2p2_ctrl, timeout); - } else { + if (ctrl->wakeup_cmd == HDMI_HDCP_STATUS_FAIL) { + hdmi_hdcp2p2_auth_failed(ctrl); + } else if (ctrl->wakeup_cmd == HDMI_HDCP_STATUS_SUCCESS) { /* Enable interrupts */ - u32 regval = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, + u32 regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2); - DEV_DBG("%s: Now authenticated. Enabling interrupts\n", - __func__); + pr_debug("Now authenticated. Enabling interrupts\n"); regval |= BIT(1); regval |= BIT(2); regval |= BIT(5); - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, - regval); - } - return rc; + DSS_REG_W(ctrl->init_data.core_io, + HDMI_HDCP_INT_CTRL2, regval); + + ctrl->init_data.notify_status( + ctrl->init_data.cb_data, + HDCP_STATE_AUTHENTICATED); + } +exit: + mutex_unlock(&ctrl->mutex); } -static int hdcp2p2_tz_error(void *client_ctx) +static void hdmi_hdcp2p2_auth_work(struct kthread_work *work) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)client_ctx; + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, auth); + enum hdcp_lib_wakeup_cmd cmd; + struct hdcp_txmtr_ops *lib = NULL; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + mutex_lock(&ctrl->mutex); + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; + } + + lib = ctrl->txmtr_ops; + if (lib && lib->wakeup) { + if (atomic_read(&ctrl->auth_state) == + HDCP_STATE_AUTHENTICATING) + cmd = HDCP_WKUP_CMD_START; + else + cmd = HDCP_WKUP_CMD_STOP; + + lib->wakeup(ctrl->lib_ctx, cmd, 0, 0); + } +exit: + mutex_unlock(&ctrl->mutex); +} + +static int hdmi_hdcp2p2_wakeup(void *client_ctx, enum hdmi_hdcp_cmd cmd, + char *msg, uint32_t msglen, uint32_t timeout) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = client_ctx; + + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - hdcp2p2_auth_failed(hdcp2p2_ctrl); + mutex_lock(&ctrl->mutex); + pr_debug("wakeup_cmd: %s\n", hdmi_hdcp_cmd_to_str(cmd)); + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; + } + + ctrl->wakeup_cmd = cmd; + ctrl->send_msg_len = msglen; + ctrl->timeout = timeout; + + if (msglen) { + ctrl->send_msg_buf = kzalloc( + ctrl->send_msg_len, GFP_KERNEL); + + if (!ctrl->send_msg_buf) + goto exit; + + memcpy(ctrl->send_msg_buf, msg, msglen); + } + + switch (ctrl->wakeup_cmd) { + case HDMI_HDCP_SEND_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->send_msg); + break; + case HDMI_HDCP_RECV_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->recv_msg); + break; + case HDMI_HDCP_STATUS_SUCCESS: + case HDMI_HDCP_STATUS_FAIL: + queue_kthread_work(&ctrl->worker, &ctrl->status); + break; + default: + pr_err("invalid wakeup command %d\n", + ctrl->wakeup_cmd); + } +exit: + mutex_unlock(&ctrl->mutex); return 0; } void hdmi_hdcp2p2_deinit(void *input) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input; + struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; struct hdcp_txmtr_ops *lib = NULL; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return; } - lib = hdcp2p2_ctrl->txmtr_ops; + lib = ctrl->txmtr_ops; - if (lib && lib->stop) - lib->stop(hdcp2p2_ctrl->hdcp_lib_handle); + if (lib && lib->wakeup) + lib->wakeup(ctrl->lib_ctx, + HDCP_WKUP_CMD_STOP, 0, 0); - sysfs_remove_group(hdcp2p2_ctrl->init_data.sysfs_kobj, - &hdcp2p2_fs_attr_group); + kthread_stop(ctrl->thread); - mutex_destroy(&hdcp2p2_ctrl->mutex); - kfree(hdcp2p2_ctrl); + sysfs_remove_group(ctrl->init_data.sysfs_kobj, + &hdmi_hdcp2p2_fs_attr_group); + + mutex_destroy(&ctrl->mutex); + kfree(ctrl); } void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl; + struct hdmi_hdcp2p2_ctrl *ctrl; int rc; static struct hdmi_hdcp_ops ops = { - .hdmi_hdcp_isr = hdcp2p2_isr, - .hdmi_hdcp_reauthenticate = hdcp2p2_reauthenticate, - .hdmi_hdcp_authenticate = hdcp2p2_authenticate, - .feature_supported = hdcp2p2_feature_supported, - .hdmi_hdcp_off = hdcp2p2_off + .hdmi_hdcp_isr = hdmi_hdcp2p2_isr, + .hdmi_hdcp_reauthenticate = hdmi_hdcp2p2_reauthenticate, + .hdmi_hdcp_authenticate = hdmi_hdcp2p2_authenticate, + .feature_supported = hdmi_hdcp2p2_feature_supported, + .hdmi_hdcp_off = hdmi_hdcp2p2_off }; static struct hdcp_client_ops client_ops = { - .hdcp_send_message = hdcp2p2_send_message_to_sink, - .hdcp_recv_message = hdcp2p2_recv_message_from_sink, - .hdcp_tz_error = hdcp2p2_tz_error, + .wakeup = hdmi_hdcp2p2_wakeup, }; static struct hdcp_txmtr_ops txmtr_ops; - DEV_DBG("%s: HDCP2P2 feature initialization\n", __func__); + pr_debug("HDCP2P2 feature initialization\n"); if (!init_data || !init_data->core_io || !init_data->mutex || !init_data->ddc_ctrl || !init_data->notify_status || !init_data->workq || !init_data->cb_data) { - DEV_ERR("%s: invalid input\n", __func__); + pr_err("invalid input\n"); return ERR_PTR(-EINVAL); } if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) { - DEV_ERR("%s: HDMI Tx does not support HDCP 2.2\n", __func__); + pr_err("HDMI Tx does not support HDCP 2.2\n"); return ERR_PTR(-ENODEV); } - hdcp2p2_ctrl = kzalloc(sizeof(*hdcp2p2_ctrl), GFP_KERNEL); - if (!hdcp2p2_ctrl) { + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) return ERR_PTR(-ENOMEM); - } - hdcp2p2_ctrl->init_data = *init_data; - hdcp2p2_ctrl->client_ops = &client_ops; - hdcp2p2_ctrl->txmtr_ops = &txmtr_ops; + ctrl->init_data = *init_data; + ctrl->client_ops = &client_ops; + ctrl->txmtr_ops = &txmtr_ops; rc = sysfs_create_group(init_data->sysfs_kobj, - &hdcp2p2_fs_attr_group); + &hdmi_hdcp2p2_fs_attr_group); if (rc) { - DEV_ERR("%s: hdcp2p2 sysfs group creation failed\n", __func__); + pr_err("hdcp2p2 sysfs group creation failed\n"); goto error; } - init_completion(&hdcp2p2_ctrl->rxstatus_completion); + init_completion(&ctrl->rxstatus_completion); - hdcp2p2_ctrl->sink_status = SINK_DISCONNECTED; + ctrl->sink_status = SINK_DISCONNECTED; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->tx_has_master_key = false; - hdcp2p2_ctrl->auth_state = HDCP_STATE_INACTIVE; - hdcp2p2_ctrl->ops = &ops; - mutex_init(&hdcp2p2_ctrl->mutex); + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); - rc = hdcp_library_register(&hdcp2p2_ctrl->hdcp_lib_handle, - hdcp2p2_ctrl->client_ops, - hdcp2p2_ctrl->txmtr_ops, hdcp2p2_ctrl); + ctrl->ops = &ops; + mutex_init(&ctrl->mutex); + + rc = hdcp_library_register(&ctrl->lib_ctx, + ctrl->client_ops, + ctrl->txmtr_ops, ctrl); if (rc) { - DEV_ERR("%s: Unable to register with HDCP 2.2 library\n", - __func__); + pr_err("Unable to register with HDCP 2.2 library\n"); goto error; } - return hdcp2p2_ctrl; + init_kthread_worker(&ctrl->worker); + init_kthread_work(&ctrl->auth, hdmi_hdcp2p2_auth_work); + init_kthread_work(&ctrl->send_msg, hdmi_hdcp2p2_send_msg_work); + init_kthread_work(&ctrl->recv_msg, hdmi_hdcp2p2_recv_msg_work); + init_kthread_work(&ctrl->status, hdmi_hdcp2p2_auth_status_work); + + ctrl->thread = kthread_run(kthread_worker_fn, + &ctrl->worker, "hdmi_hdcp2p2"); + + if (IS_ERR(ctrl->thread)) { + pr_err("unable to start hdcp2p2 thread\n"); + rc = PTR_ERR(ctrl->thread); + ctrl->thread = NULL; + goto error; + } + + return ctrl; error: - kfree(hdcp2p2_ctrl); + kfree(ctrl); return ERR_PTR(rc); } -static bool hdcp2p2_supported(struct hdcp2p2_ctrl *hdcp2p2_ctrl) +static bool hdmi_hdcp2p2_supported(struct hdmi_hdcp2p2_ctrl *ctrl) { u8 hdcp2version; - int rc = hdcp2p2_read_version(hdcp2p2_ctrl, &hdcp2version); + int rc = hdmi_hdcp2p2_read_version(ctrl, &hdcp2version); if (rc) goto error; if (hdcp2version & BIT(2)) { - DEV_DBG("%s: Sink is HDCP 2.2 capable\n", __func__); + pr_debug("Sink is HDCP 2.2 capable\n"); return true; } error: - DEV_DBG("%s: Sink is not HDCP 2.2 capable\n", __func__); + pr_debug("Sink is not HDCP 2.2 capable\n"); return false; } struct hdmi_hdcp_ops *hdmi_hdcp2p2_start(void *input) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; + struct hdmi_hdcp2p2_ctrl *ctrl = input; - DEV_DBG("%s: Checking sink capability\n", __func__); - if (hdcp2p2_supported(hdcp2p2_ctrl)) - return hdcp2p2_ctrl->ops; + pr_debug("Checking sink capability\n"); + if (hdmi_hdcp2p2_supported(ctrl)) + return ctrl->ops; else return NULL; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 20bcf3e5fa9f..53c8006b0071 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -633,6 +633,7 @@ again: time_out_count = wait_for_completion_timeout( &ddc_ctrl->ddc_sw_done, wait_time); + DEV_DBG("ddc read done at %dms\n", jiffies_to_msecs(jiffies)); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, BIT(1)); if (!time_out_count) { @@ -1089,6 +1090,8 @@ again: time_out_count = wait_for_completion_timeout( &ddc_ctrl->ddc_sw_done, HZ/2); + DEV_DBG("DDC write done at %dms\n", jiffies_to_msecs(jiffies)); + reg_val = DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, reg_val & (~BIT(2))); if (!time_out_count) { diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index 0a31b66e6a72..8d7f19ad97ff 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -14,25 +14,74 @@ #define __HDCP_QSEECOM_H #include +enum hdcp_lib_wakeup_cmd { + HDCP_WKUP_CMD_START, + HDCP_WKUP_CMD_STOP, + HDCP_WKUP_CMD_MSG_SEND_SUCCESS, + HDCP_WKUP_CMD_MSG_SEND_FAILED, + HDCP_WKUP_CMD_MSG_RECV_SUCCESS, + HDCP_WKUP_CMD_MSG_RECV_FAILED, + HDCP_WKUP_CMD_MSG_RECV_TIMEOUT, +}; + +enum hdmi_hdcp_cmd { + HDMI_HDCP_SEND_MESSAGE, + HDMI_HDCP_RECV_MESSAGE, + HDMI_HDCP_STATUS_SUCCESS, + HDMI_HDCP_STATUS_FAIL +}; + +static inline char *hdmi_hdcp_cmd_to_str(uint32_t cmd) +{ + switch (cmd) { + case HDMI_HDCP_SEND_MESSAGE: + return "HDMI_HDCP_SEND_MESSAGE"; + case HDMI_HDCP_RECV_MESSAGE: + return "HDMI_HDCP_RECV_MESSAGE"; + case HDMI_HDCP_STATUS_SUCCESS: + return "HDMI_HDCP_STATUS_SUCCESS"; + case HDMI_HDCP_STATUS_FAIL: + return "HDMI_HDCP_STATUS_FAIL"; + default: + return "???"; + } +} + +static inline char *hdcp_cmd_to_str(uint32_t cmd) +{ + switch (cmd) { + case HDCP_WKUP_CMD_START: + return "HDCP_WKUP_CMD_START"; + case HDCP_WKUP_CMD_STOP: + return "HDCP_WKUP_CMD_STOP"; + case HDCP_WKUP_CMD_MSG_SEND_SUCCESS: + return "HDCP_WKUP_CMD_MSG_SEND_SUCCESS"; + case HDCP_WKUP_CMD_MSG_SEND_FAILED: + return "HDCP_WKUP_CMD_MSG_SEND_FAILED"; + case HDCP_WKUP_CMD_MSG_RECV_SUCCESS: + return "HDCP_WKUP_CMD_MSG_RECV_SUCCESS"; + case HDCP_WKUP_CMD_MSG_RECV_FAILED: + return "HDCP_WKUP_CMD_MSG_RECV_FAILED"; + case HDCP_WKUP_CMD_MSG_RECV_TIMEOUT: + return "HDCP_WKUP_CMD_MSG_RECV_TIMEOUT"; + default: + return "???"; + } +} + struct hdcp_txmtr_ops { - int (*start)(void *phdcpcontext); - int (*stop)(void *phdcpcontext); + int (*wakeup)(void *phdcpcontext, enum hdcp_lib_wakeup_cmd cmd, + char *msg, uint32_t len); bool (*feature_supported)(void *phdcpcontext); - int (*process_message)(void *phdcpcontext, - unsigned char *msg, uint32_t msg_size); int (*hdcp_txmtr_get_state)(void *phdcpcontext, uint32_t *state); - int (*hdcp_txmtr_query_stream_type)(void *phdcpcontext); + int (*hdcp_query_stream_type)(void *phdcpcontext); }; struct hdcp_client_ops { - int (*hdcp_send_message)(void *client_ctx, - char *message, uint32_t msg_size); - int (*hdcp_recv_message)(void *client_ctx, - char *message, uint32_t msg_size, - u32 timeout); - int (*hdcp_tz_error)(void *client_ctx); + int (*wakeup)(void *client_ctx, enum hdmi_hdcp_cmd cmd, + char *msg, uint32_t msglen, uint32_t timeout); }; int hdcp_library_register(void **pphdcpcontext,