From 1afd631cba59ca35d4aba7c393ff5788235b4f60 Mon Sep 17 00:00:00 2001 From: Haesu Gwon Date: Thu, 29 May 2025 17:16:13 +0900 Subject: [PATCH] [HALACR-50] Add HAL interface for HDCP - Add "doc" directory for documentation. - Add new HDCP HAL interface. [Version] 1.0.0 [Issue Type] New feature Change-Id: Iada815ba26854b03d25eddea89912958f4406c5b --- CMakeLists.txt | 51 ++ LICENSE | 203 ++++++ doc/hal_hdcp_doc.h | 83 +++ doc/images/hal_drm_state_diagram.png | Bin 0 -> 44282 bytes hal-api-hdcp-haltests.manifest | 6 + hal-api-hdcp.manifest | 6 + hal-api-hdcp.pc.in | 13 + include/hal-hdcp-interface-1.h | 202 ++++++ include/hal-hdcp-interface.h | 24 + include/hal-hdcp.h | 300 +++++++++ packaging/hal-api-hdcp-manifest.xml | 8 + packaging/hal-api-hdcp.spec | 84 +++ src/hal-api-hdcp.c | 431 +++++++++++++ tests/CMakeLists.txt | 21 + tests/hdcp_hal_test.cpp | 912 +++++++++++++++++++++++++++ 15 files changed, 2344 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 doc/hal_hdcp_doc.h create mode 100644 doc/images/hal_drm_state_diagram.png create mode 100644 hal-api-hdcp-haltests.manifest create mode 100644 hal-api-hdcp.manifest create mode 100644 hal-api-hdcp.pc.in create mode 100644 include/hal-hdcp-interface-1.h create mode 100644 include/hal-hdcp-interface.h create mode 100644 include/hal-hdcp.h create mode 100644 packaging/hal-api-hdcp-manifest.xml create mode 100644 packaging/hal-api-hdcp.spec create mode 100644 src/hal-api-hdcp.c create mode 100644 tests/CMakeLists.txt create mode 100644 tests/hdcp_hal_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5e5937a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) +PROJECT(hal-api-hdcp) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(EXEC_PREFIX "${CMAKE_INSTALL_PREFIX}/bin") +SET(INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") +SET(LIBDIR ${CMAKE_LIBDIR_PREFIX}) +SET(VERSION_MAJOR 0) +SET(VERSION "${VERSION_MAJOR}.1.0") + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) + +SET(PKG_MODULES + dlog + glib-2.0 + hal-api-common +) + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED ${PKG_MODULES}) + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions") +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fPIC -Wall -Wextra -Wno-array-bounds -Wno-empty-body -Wno-ignored-qualifiers -Wno-unused-parameter -Wshadow -Wwrite-strings -Wswitch-default -Wno-unused-but-set-parameter -Wno-unused-but-set-variable") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -lrt") + +SET(SRCS src/hal-api-hdcp.c) + +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS}) + +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} -Wl,--as-needed -Wl,--rpath=${LIBDIR}/hal) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${VERSION}) + +CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIBDIR}/hal) + +INSTALL(DIRECTORY include/ DESTINATION include/hal + FILES_MATCHING + PATTERN "*_private.h" EXCLUDE + PATTERN "include/*.h") + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIBDIR}/pkgconfig) + +ADD_SUBDIRECTORY(tests) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..36123b0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) 2000 - 2025 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/doc/hal_hdcp_doc.h b/doc/hal_hdcp_doc.h new file mode 100644 index 0000000..af50bd2 --- /dev/null +++ b/doc/hal_hdcp_doc.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __TIZEN_HAL_HDCP_DOC_H__ +#define __TIZEN_HAL_HDCP_DOC_H__ + + +/** + * @file hal_hdcp_doc.h + * @brief This file contains high level documentation of the HAL HDCP. + */ + +/** + * @defgroup HALAPI_HAL_HDCP_MODULE HDCP + * @brief The @ref HALAPI_HAL_HDCP_MODULE provides functions for hdcp devices. + * + * @section HALAPI_HAL_HDCP_MODULE_HEADER Required Header + * \#include + * + * @section HALAPI_HAL_HDCP_MODULE_OVERVIEW Overview + * The hdcp devices have various specifications, so it's hard to control them using single code. + * The hdcp HAL provides common abstraction interfaces to control hdcp devices which are different. + * + * The HDCP HAL allows creation of components required in secure playback including: + * - managing hdcp sessions. + * - decrypt hdcp content. + * - encrypt content. + * + * @subsection HALAPI_HAL_HDCP_MODULE_STATE_DIAGRAM State Diagram + * @image html hal_hdcp_state_diagram.png + * + * @subsection HALAPI_HAL_HDCP_MODULE_STATE_TRANSITIONS State Transitions + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
FUNCTIONPRE-STATEPOST-STATESYNC TYPE
hal_hdcp_init() NONE INITIALIZED SYNC
hal_hdcp_deinit() INITIALIZED NONE SYNC
hal_hdcp_open() INITIALIZED OPENED SYNC
hal_hdcp_close() OPENED INITIALIZED SYNC
+ * + * For more information on the HDCP features and the macros, see HAL HDCP programming guides and tutorials. + */ + +#endif /* __TIZEN_HAL_HDCP_DOC_H__ */ diff --git a/doc/images/hal_drm_state_diagram.png b/doc/images/hal_drm_state_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8647fe20658a65d288f484e67ff37c738055e2 GIT binary patch literal 44282 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 CCHkrW literal 0 HcmV?d00001 diff --git a/hal-api-hdcp-haltests.manifest b/hal-api-hdcp-haltests.manifest new file mode 100644 index 0000000..41a9320 --- /dev/null +++ b/hal-api-hdcp-haltests.manifest @@ -0,0 +1,6 @@ + + + + + + diff --git a/hal-api-hdcp.manifest b/hal-api-hdcp.manifest new file mode 100644 index 0000000..41a9320 --- /dev/null +++ b/hal-api-hdcp.manifest @@ -0,0 +1,6 @@ + + + + + + diff --git a/hal-api-hdcp.pc.in b/hal-api-hdcp.pc.in new file mode 100644 index 0000000..484072c --- /dev/null +++ b/hal-api-hdcp.pc.in @@ -0,0 +1,13 @@ +# Package Information for pkg-config +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@/hal +libdir=@LIBDIR@/hal +includedir=@INCLUDEDIR@/hal + +Name: @PROJECT_NAME@ +Description: @PROJECT_NAME@ interface +Version: @VERSION@ +Requires: +Libs: -L${libdir} -l@PROJECT_NAME@ +Cflags: -I${includedir} +CXXflags: -I${includedir} diff --git a/include/hal-hdcp-interface-1.h b/include/hal-hdcp-interface-1.h new file mode 100644 index 0000000..1bc1d80 --- /dev/null +++ b/include/hal-hdcp-interface-1.h @@ -0,0 +1,202 @@ +/* + * HAL (Hardware Abstract Layer) interface API for HDCP + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HAL_HDCP_INTERFACE_1__ +#define __HAL_HDCP_INTERFACE_1__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file hal-hdcp-interface-1.h + * @brief This file contains the Tizen HDCP HAL interface API, related structures and enumerations. + * @since HAL_MODULE_HDCP 1.0 + */ + +/** + * @addtogroup HALAPI_HAL_HDCP_MODULE + * @{ + */ + +/** + * @brief Enumeration for the HDCP error. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef enum hal_hdcp_error { + HAL_HDCP_ERROR_NONE = 0, /**< Error none */ + HAL_HDCP_ERROR_NOT_SUPPORTED = (int32_t)0x80004001, /**< Not supported */ + HAL_HDCP_ERROR_INVALID_PARAMETER = (int32_t)0x80004002, /**< Invalid parameter */ + HAL_HDCP_ERROR_INVALID_STATE = (int32_t)0x80004003, /**< Invalid state */ + HAL_HDCP_ERROR_OUT_OF_MEMORY = (int32_t)0x80004004, /**< Out of memory */ + + HAL_HDCP_ERROR_INTERNAL = (int32_t)0x80004ffd, /**< Internal */ + HAL_HDCP_ERROR_NOT_IMPLEMENTED = (int32_t)0x80004ffe, /**< Not implemented */ + HAL_HDCP_ERROR_UNKNOWN = (int32_t)0x80004fff /**< Unknown */ +} hal_hdcp_error_e; + +/** + * @brief Enumeration for the HDCP state. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef enum hal_hdcp_state { + HAL_HDCP_STATE_NONE, /**< None state */ + HAL_HDCP_STATE_INITIALIZED, /**< Initialized state for HDCP device */ + HAL_HDCP_STATE_OPENED, /**< Opened state for HDCP device */ +} hal_hdcp_state_e; + +/** + * @brief The structure type of HDCP buffer. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef struct { + bool is_secure; /**< The flag which indicates the real data is managed in secure world or not */ + + uint32_t size; /**< The size of data buffer */ + unsigned char *data; /**< The pointer of data buffer */ + int fd; /**< The fd(file descriptor) of data buffer */ +} hal_hdcp_buffer_s; + +/** + * @brief Enumeration for the HDCP command. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef enum hal_hdcp_command { + HAL_HDCP_COMMAND_BASE = (uint64_t)0x0000000000000001, /**< Base of command */ + HAL_HDCP_COMMAND_CUSTOM = (uint64_t)0xf000000000000000 /**< Custom */ +} hal_hdcp_command_e; + +/** + * @brief The structure type of the HDCP HAL custom command. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef struct { + const char *name; /**< The name of custom command */ + void *value; /**< The value of custom command */ +} hal_hdcp_custom_command_s; + +/** + * @brief The structure type of batch command. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef struct hal_hdcp_batch_command_control { + uint64_t command_set_flag; /**< flag for updating commands */ + + hal_hdcp_custom_command_s custom; /**< The value for custom command */ +} hal_hdcp_batch_command_control_s; + +/** + * @brief Enumeration for the HDCP message type. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef enum hal_hdcp_message_type { + HAL_HDCP_MESSAGE_TYPE_ERROR, /**< The error */ +} hal_hdcp_message_type_e; + +/** + * @brief The structure type of the HDCP message. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef struct hal_hdcp_message { + hal_hdcp_message_type_e type; /**< The type of message */ + union { + hal_hdcp_error_e error_code; /**< The error code */ + }; +} hal_hdcp_message_s; + +/** + * @brief Callback function for notification from HDCP HAL. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] message The message from HDCP HAL + * @param[in] user_data The user data for callback + * @see hal_hdcp_add_message_callback() + * @see hal_hdcp_remove_message_callback() + */ +typedef hal_hdcp_error_e (*hal_hdcp_message_cb)(hal_hdcp_message_s *message, void *user_data); + +/* HDCP */ + +/** + * @brief HDCP device type + * @since HAL_MODULE_HDCP 1.0 + */ +typedef enum +{ + HAL_HDCP_DEVICE_RECEIVER, /**< Receiver */ + HAL_HDCP_DEVICE_TRANSMIT, /**< Transmitter */ + HAL_HDCP_DEVICE_TRANSMIT_MULTISINK, /**< Transmitter with multi sink */ + } hal_hdcp_device_type_e; + +/** + * @brief HDCP protocol version + * @since HAL_MODULE_HDCP 1.0 + */ +typedef enum +{ + HAL_HDCP_VERSION_2_0 = 20, /**< HDCP version 2.0 */ + HAL_HDCP_VERSION_2_1 = 21, /**< HDCP version 2.1 */ + HAL_HDCP_VERSION_2_2 = 22, /**< HDCP version 2.2 */ +} hal_hdcp_protocol_version_e; + +/** + * @brief The structure type of the HDCP HAL functions. + * @since HAL_MODULE_HDCP 1.0 + */ +typedef struct _hal_backend_hdcp_funcs { + /** Initializes HDCP HAL backend handle */ + int (*init)(void **hdcp_handle); + /** Deinitializes HDCP HAL backend handle */ + int (*deinit)(void *hdcp_handle); + /** Opens HDCP */ + int (*hdcp_open)(void *hdcp_handle, hal_hdcp_device_type_e type, hal_hdcp_protocol_version_e version); + /** Closes HDCP */ + int (*hdcp_close)(void *hdcp_handle); + /** Starts HDCP receiver */ + int (*hdcp_start_receiver)(void *hdcp_handle, uint32_t socket_port, uint32_t *hdcp_id); + /** Stops HDCP receiver */ + int (*hdcp_stop_receiver)(void *hdcp_handle, uint32_t hdcp_id); + /** Starts HDCP transmitter */ + int (*hdcp_start_transmitter)(void *hdcp_handle, const char *socket_ip, uint32_t socket_port, uint32_t *hdcp_id); + /** Stops HDCP transmitter */ + int (*hdcp_stop_transmitter)(void *hdcp_handle, uint32_t hdcp_id); + /** Allocates hdcp output buffer */ + int (*hdcp_allocate_output_buffer)(void *hdcp_handle, uint32_t size, bool is_secure, hal_hdcp_buffer_s *buffer); + /** Releases hdcp output buffer */ + int (*hdcp_release_output_buffer)(void *hdcp_handle, hal_hdcp_buffer_s *buffer); + /** Decrypts HDCP data */ + int (*hdcp_decrypt)(void *hdcp_handle, hal_hdcp_buffer_s *input, void *decrypt_info, hal_hdcp_buffer_s *output); + /** Encrypts HDCP data */ + int (*hdcp_encrypt)(void *hdcp_handle, hal_hdcp_buffer_s *input, uint32_t hdcp_id, void *encrypt_info, hal_hdcp_buffer_s *output); + /** Sets the various command and value */ + int (*set_command)(void *hdcp_handle, hal_hdcp_command_e command, void *value); + /** Gets the current value of command */ + int (*get_command)(void *hdcp_handle, hal_hdcp_command_e command, void **value); + /** Sets a set of commands */ + int (*set_batch_command)(void *hdcp_handle, hal_hdcp_batch_command_control_s *batch_command, hal_hdcp_command_e *error_command); +} hal_backend_hdcp_funcs; + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* __HAL_HDCP_INTERFACE_1__ */ diff --git a/include/hal-hdcp-interface.h b/include/hal-hdcp-interface.h new file mode 100644 index 0000000..8f7e1d1 --- /dev/null +++ b/include/hal-hdcp-interface.h @@ -0,0 +1,24 @@ +/* + * HAL (Hardware Abstract Layer) interface API for HDCP + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HAL_HDCP_INTERFACE__ +#define __HAL_HDCP_INTERFACE__ + +#include + +#endif /* __HAL_HDCP_INTERFACE__ */ diff --git a/include/hal-hdcp.h b/include/hal-hdcp.h new file mode 100644 index 0000000..5a6fc77 --- /dev/null +++ b/include/hal-hdcp.h @@ -0,0 +1,300 @@ +/* + * HAL(Hardware Abstract Layer) API for HDCP(High-bandwidth Digital Content Protection) + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HAL_HDCP__ +#define __HAL_HDCP__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file hal-hdcp.h + * @brief This file contains the Tizen HDCP HAL API, related structures and enumerations. + * @since HAL_MODULE_HDCP 1.0 + */ + +/** + * @addtogroup HAL_HDCP_MODULE + * @{ + */ + +/** + * @brief Initializes new handle of HDCP HAL. + * @since HAL_MODULE_HDCP 1.0 + * @param[out] hdcp_handle A newly returned handle to the HDCP HAL + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_OUT_OF_MEMORY Out of memory + * @retval #HAL_HDCP_ERROR_INTERNAL Internal error + * @post If it succeeds, the HDCP state will be #HAL_HDCP_STATE_INITIALIZED. + * @see hal_hdcp_deinit() + */ +int hal_hdcp_init(void **hdcp_handle); + +/** + * @brief Deinitializes handle of HDCP HAL. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @post If it succeeds, the HDCP state will be #HAL_HDCP_STATE_NONE. + * @see hal_hdcp_init() + */ +int hal_hdcp_deinit(void *hdcp_handle); + +/** + * @brief Opens HDCP. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] type The type of HDCP + * @param[in] version The version of HDCP + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_INITIALIZED. + * @post If it succeeds, the HDCP state will be #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_close() + */ +int hal_hdcp_open(void *hdcp_handle, hal_hdcp_device_type_e type, hal_hdcp_protocol_version_e version); + +/** + * @brief Closes HDCP. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @post If it succeeds, the HDCP state will be #HAL_HDCP_STATE_INITIALIZED. + * @see hal_hdcp_open() + */ +int hal_hdcp_close(void *hdcp_handle); + +/** + * @brief Starts HDCP receiver. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] socket_port The socket port number + * @param[out] hdcp_id The hdcp id + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_stop_receiver() + */ +int hal_hdcp_start_receiver(void *hdcp_handle, uint32_t socket_port, uint32_t *hdcp_id); + +/** + * @brief Stops HDCP receiver. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] hdcp_id The hdcp id + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_start_receiver() + */ +int hal_hdcp_stop_receiver(void *hdcp_handle, uint32_t hdcp_id); + +/** + * @brief Starts HDCP transmitter. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] socket_ip The socket ip address + * @param[in] socket_port The socket port number + * @param[out] hdcp_id The hdcp id + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_stop_transmitter() + */ +int hal_hdcp_start_transmitter(void *hdcp_handle, const char *socket_ip, uint32_t socket_port, uint32_t *hdcp_id); + +/** + * @brief Stops HDCP transmitter. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] hdcp_id The hdcp id + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_start_transmitter() + */ +int hal_hdcp_stop_transmitter(void *hdcp_handle, uint32_t hdcp_id); + +/** + * @brief Allocates HDCP output buffer. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] size The size of output buffer + * @param[in] is_secure The flag which indicates the real data is managed in secure world or not + * @param[out] output The HDCP output buffer + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_INTERNAL Internal error + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_release_output_buffer() + * @see hal_hdcp_decrypt() + */ +int hal_hdcp_allocate_output_buffer(void *hdcp_handle, uint32_t size, bool is_secure, hal_hdcp_buffer_s *buffer); + +/** + * @brief Releases HDCP output buffer. + * @since HAL_MODULE_HDCP 1.0 + * @remarks The output buffer should be released with this function after use it. + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] output The HDCP output buffer to release + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_allocate_output_buffer() + * @see hal_hdcp_decrypt() + */ +int hal_hdcp_release_output_buffer(void *hdcp_handle, hal_hdcp_buffer_s *buffer); + +/** + * @brief Decrypts HDCP data. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] input The input buffer to decrypt + * @param[in] decrypt_info The decryption information + * @param[out] output The decrypted output buffer + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_INTERNAL Internal error + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_allocate_output_buffer() + * @see hal_hdcp_release_output_buffer() + */ +int hal_hdcp_decrypt(void *hdcp_handle, hal_hdcp_buffer_s *input, void *decrypt_info, hal_hdcp_buffer_s *output); + +/** + * @brief Encrypts HDCP data to send. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] input The input buffer to encrypt + * @param[in] hdcp_id The hdcp id + * @param[in] encrypt_info The encryption information + * @param[out] output The encrypted output buffer + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_INTERNAL Internal error + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_OPENED. + * @see hal_hdcp_allocate_output_buffer() + * @see hal_hdcp_release_output_buffer() + */ +int hal_hdcp_encrypt(void *hdcp_handle, hal_hdcp_buffer_s *input, uint32_t hdcp_id, void *encrypt_info, hal_hdcp_buffer_s *output); + +/** + * @brief Gets the state of HDCP. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[out] state The current state of the HDCP + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_INITIALIZED. + */ +int hal_hdcp_get_state(void *hdcp_handle, hal_hdcp_state_e *state); + +/** + * @brief Sets the various command and value to control HDCP. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] command The command to control the HDCP device + * @param[in] value The value to set + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_NOT_SUPPORTED The command is not supported + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_INVALID_STATE Invalid state + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_INITIALIZED. + * @see hal_hdcp_get_command() + */ +int hal_hdcp_set_command(void *hdcp_handle, hal_hdcp_command_e command, void *value); + +/** + * @brief Gets the current value of command. + * @since HAL_MODULE_HDCP 1.0 + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] command The command to control the HDCP device + * @param[out] value The value to get + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_NOT_SUPPORTED The command is not supported + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_INVALID_STATE Invalid state + * @retval #HAL_HDCP_ERROR_INTERNAL Internal error + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_INITIALIZED. + * @see hal_hdcp_set_command() + */ +int hal_hdcp_get_command(void *hdcp_handle, hal_hdcp_command_e command, void **value); + +/** + * @brief Sets a set of commands. + * @since HAL_MODULE_HDCP 1.0 + * @remarks error_command will be set if error is returned from the function. + * @param[in] hdcp_handle The handle to the HDCP HAL + * @param[in] batch_command The batch command to set + * @param[out] error_command The error command + * @return @c 0 on success, otherwise a negative error value + * @retval #HAL_HDCP_ERROR_NONE Successful + * @retval #HAL_HDCP_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #HAL_HDCP_ERROR_INVALID_STATE Invalid state + * @retval #HAL_HDCP_ERROR_NOT_IMPLEMENTED The feature is not implemented + * @pre The HDCP state must be set to #HAL_HDCP_STATE_INITIALIZED. + * @see hal_hdcp_set_command() + * @see hal_hdcp_get_command() + */ +int hal_hdcp_set_batch_command(void *hdcp_handle, hal_hdcp_batch_command_control_s *batch_command, hal_hdcp_command_e *error_command); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __HAL_HDCP__ */ diff --git a/packaging/hal-api-hdcp-manifest.xml b/packaging/hal-api-hdcp-manifest.xml new file mode 100644 index 0000000..550f1a5 --- /dev/null +++ b/packaging/hal-api-hdcp-manifest.xml @@ -0,0 +1,8 @@ + + + + HAL_MODULE_HDCP + 1.0 + + + diff --git a/packaging/hal-api-hdcp.spec b/packaging/hal-api-hdcp.spec new file mode 100644 index 0000000..a246805 --- /dev/null +++ b/packaging/hal-api-hdcp.spec @@ -0,0 +1,84 @@ +%define name hal-api-hdcp + +### main package ######### +Name: %{name} +Summary: %{name} interface +Version: 1.0.1 +Release: 0 +Group: Development/Libraries +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +Source1: %{name}-manifest.xml +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +BuildRequires: cmake +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(hal-api-common) +BuildRequires: pkgconfig(gmock) +BuildRequires: pkgconfig(capi-system-info) + +%description +%{name} interface package for product vendor developer + + +### devel package ######### +%package devel +Summary: %{name} interface +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +%{name} interface development package for product vendor developer + +### test package ######### +%package haltests +Summary: tests for %{name} +Requires: %{name} = %{version} + +%description haltests +Haltests for %{name} + + +### build and install ######### +%prep +%setup -q + +%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DCMAKE_LIBDIR_PREFIX=%{_libdir} + +%build +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sysconfdir}/hal +cp %{SOURCE1} %{buildroot}%{_sysconfdir}/hal/ +%make_install + +%clean +rm -rf %{buildroot} + +%post +/sbin/ldconfig +/usr/bin/hal-compatibility-checker --reset + +%postun +/sbin/ldconfig +/usr/bin/hal-compatibility-checker --reset + +%files +%manifest %{name}.manifest +%license LICENSE +%defattr(-,root,root,-) +%{_libdir}/hal/*.so.* +%{_sysconfdir}/hal/%{name}-manifest.xml + +%files devel +%defattr(-,root,root,-) +%{_includedir}/hal/*.h +%{_libdir}/hal/*.so +%{_libdir}/pkgconfig/*.pc + +%files haltests +%manifest %{name}-haltests.manifest +%{_bindir}/hal/hdcp-haltests diff --git a/src/hal-api-hdcp.c b/src/hal-api-hdcp.c new file mode 100644 index 0000000..b5c0252 --- /dev/null +++ b/src/hal-api-hdcp.c @@ -0,0 +1,431 @@ +/* + * HAL (Hardware Abstract Layer) API HDCP + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include "hal-hdcp-interface.h" +#include "hal-hdcp.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "HALAPI_HDCP" + +#define HAL_HDCP_RETURN_IF_NULL(arg, ret) \ +do { \ + if (!(arg)) { \ + SLOGE("[%s]failed, return[%s]", #arg, #ret); \ + return (ret); \ + } \ +} while (0) + +#define HAL_HDCP_CHECK_STATE(state, target_state) \ +do { \ + if ((state < target_state)) { \ + SLOGE("should be [%d]state", target_state); \ + return HAL_HDCP_ERROR_INVALID_STATE; \ + } \ +} while (0) + +#define AUTOCLEAN_LOCKER(x) g_autoptr(GMutexLocker) locker = g_mutex_locker_new(x); +#define AUTOCLEAN_UNLOCKER() g_clear_pointer(&locker, g_mutex_locker_free); + +typedef struct _hal_hdcp_s { + void *backend; + hal_backend_hdcp_funcs *funcs; + hal_hdcp_state_e state; + GMutex lock; +} hal_hdcp_s; + +static void __hal_hdcp_set_state(hal_hdcp_s *handle, hal_hdcp_state_e state) +{ + LOGI("Current state : %d, Target state : %d", handle->state, state); + handle->state = state; +} + +static int hal_hdcp_init_backend(void **data, void *user_data) +{ + if (!data) { + SLOGE("NULL data"); + return -EINVAL; + } + + *data = g_new0(hal_backend_hdcp_funcs, 1); + + SLOGI("new - hdcp HAL funcs[%p], size[%zu]", + *data, sizeof(hal_backend_hdcp_funcs)); + + return 0; +} + +static int hal_hdcp_exit_backend(void *data, void *user_data) +{ + if (!data) { + SLOGE("NULL data"); + return -EINVAL; + } + + SLOGI("release - hdcp HAL funcs[%p], size[%zu]", + data, sizeof(hal_backend_hdcp_funcs)); + + g_free(data); + + return 0; +} + +int hal_hdcp_init(void **hdcp_handle) +{ + HAL_HDCP_RETURN_IF_NULL(hdcp_handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + + SLOGI("start"); + + hal_hdcp_s *new_handle = g_new0(hal_hdcp_s, 1); + + g_mutex_init(&new_handle->lock); + AUTOCLEAN_LOCKER(&new_handle->lock); + + int ret = hal_common_get_backend_v2(HAL_MODULE_HDCP, + (void **)&new_handle->funcs, + NULL, + hal_hdcp_init_backend); + if (ret != 0) { + SLOGE("Failed to get backend"); + goto __HAL_INIT_FAILED; + } + + if (!new_handle->funcs || !new_handle->funcs->init) { + SLOGE("invalid ptr[%p]", new_handle->funcs); + goto __HAL_INIT_FAILED; + } + + ret = new_handle->funcs->init(&new_handle->backend); + if (ret != HAL_HDCP_ERROR_NONE) { + SLOGE("backend init failed[0x%x]", ret); + goto __HAL_INIT_FAILED; + } + + __hal_hdcp_set_state(new_handle, HAL_HDCP_STATE_INITIALIZED); + + *hdcp_handle = (void *)new_handle; + + SLOGI("done"); + + return HAL_HDCP_ERROR_NONE; + +__HAL_INIT_FAILED: + if (new_handle->funcs) { + hal_common_put_backend_v2(HAL_MODULE_HDCP, + (void *)new_handle->funcs, + NULL, + hal_hdcp_exit_backend); + } + + g_free(new_handle); + + return HAL_HDCP_ERROR_INTERNAL; +} + +int hal_hdcp_deinit(void *hdcp_handle) +{ + int ret = 0; + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->deinit, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + + SLOGI("start handle=%p", hdcp_handle); + + AUTOCLEAN_LOCKER(&handle->lock); + + ret = handle->funcs->deinit(handle->backend); + if (ret != HAL_HDCP_ERROR_NONE) { + SLOGE("deinit failed[0x%x]", ret); + return ret; + } + + hal_common_put_backend_v2(HAL_MODULE_HDCP, + (void *)handle->funcs, + NULL, + hal_hdcp_exit_backend); + + AUTOCLEAN_UNLOCKER(); + g_mutex_clear(&handle->lock); + + g_free(handle); + handle = NULL; + + SLOGI("done"); + + return HAL_HDCP_ERROR_NONE; +} + +int hal_hdcp_open(void *hdcp_handle, hal_hdcp_device_type_e type, hal_hdcp_protocol_version_e version) +{ + int ret = 0; + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_open, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + + if (type < HAL_HDCP_DEVICE_RECEIVER || type > HAL_HDCP_DEVICE_TRANSMIT_MULTISINK) + return HAL_HDCP_ERROR_INVALID_PARAMETER; + + if (version < HAL_HDCP_VERSION_2_0 || version > HAL_HDCP_VERSION_2_2) + return HAL_HDCP_ERROR_INVALID_PARAMETER; + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_INITIALIZED); + + ret = handle->funcs->hdcp_open(handle->backend, type, version); + if (ret == HAL_HDCP_ERROR_NONE) + __hal_hdcp_set_state(handle, HAL_HDCP_STATE_OPENED); + + return ret; +} + +int hal_hdcp_close(void *hdcp_handle) +{ + int ret = 0; + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_close, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + ret = handle->funcs->hdcp_close(handle->backend); + if (ret == HAL_HDCP_ERROR_NONE) + __hal_hdcp_set_state(handle, HAL_HDCP_STATE_INITIALIZED); + + return ret; +} + +int hal_hdcp_start_receiver(void *hdcp_handle, uint32_t socket_port, uint32_t *hdcp_id) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_start_receiver, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(hdcp_id, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_start_receiver(handle->backend, socket_port, hdcp_id); +} + +int hal_hdcp_stop_receiver(void *hdcp_handle, uint32_t hdcp_id) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_stop_receiver, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_stop_receiver(handle->backend, hdcp_id); +} + +int hal_hdcp_start_transmitter(void *hdcp_handle, const char *socket_ip, uint32_t socket_port, uint32_t *hdcp_id) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_start_transmitter, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(socket_ip, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(hdcp_id, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_start_transmitter(handle->backend, socket_ip, socket_port, hdcp_id); +} + +int hal_hdcp_stop_transmitter(void *hdcp_handle, uint32_t hdcp_id) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_stop_transmitter, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_stop_transmitter(handle->backend, hdcp_id); +} + +int hal_hdcp_allocate_output_buffer(void *hdcp_handle, uint32_t size, bool is_secure, hal_hdcp_buffer_s *buffer) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_stop_transmitter, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(buffer, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_allocate_output_buffer(handle->backend, size, is_secure, buffer); +} + +int hal_hdcp_release_output_buffer(void *hdcp_handle, hal_hdcp_buffer_s *buffer) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_stop_transmitter, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(buffer, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_release_output_buffer(handle->backend, buffer); +} + +int hal_hdcp_decrypt(void *hdcp_handle, hal_hdcp_buffer_s *input, void *decrypt_info, hal_hdcp_buffer_s *output) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_decrypt, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(input, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(output, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_decrypt(handle->backend, input, decrypt_info, output); +} + +int hal_hdcp_encrypt(void *hdcp_handle, hal_hdcp_buffer_s *input, uint32_t hdcp_id, void *encrypt_info, hal_hdcp_buffer_s *output) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->hdcp_encrypt, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(input, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(encrypt_info, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(output, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_OPENED); + + return handle->funcs->hdcp_encrypt(handle->backend, input, hdcp_id, encrypt_info, output); +} + +int hal_hdcp_get_state(void *hdcp_handle, hal_hdcp_state_e *state) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(state, HAL_HDCP_ERROR_INVALID_PARAMETER); + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_INITIALIZED); + + *state = handle->state; + + return HAL_HDCP_ERROR_NONE; +} + +int hal_hdcp_set_command(void *hdcp_handle, hal_hdcp_command_e command, void *value) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->set_command, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(value, HAL_HDCP_ERROR_INVALID_PARAMETER); + + if (command < HAL_HDCP_COMMAND_BASE || command > HAL_HDCP_COMMAND_CUSTOM) + return HAL_HDCP_ERROR_INVALID_PARAMETER; + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_INITIALIZED); + + return handle->funcs->set_command(handle->backend, command, value); +} + +int hal_hdcp_get_command(void *hdcp_handle, hal_hdcp_command_e command, void **value) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->get_command, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(value, HAL_HDCP_ERROR_INVALID_PARAMETER); + + if (command < HAL_HDCP_COMMAND_BASE || command > HAL_HDCP_COMMAND_CUSTOM) + return HAL_HDCP_ERROR_INVALID_PARAMETER; + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_INITIALIZED); + + return handle->funcs->get_command(handle->backend, command, value); +} + +int hal_hdcp_set_batch_command(void *hdcp_handle, hal_hdcp_batch_command_control_s *batch_command, hal_hdcp_command_e *error_command) +{ + hal_hdcp_s *handle = (hal_hdcp_s *)hdcp_handle; + + HAL_HDCP_RETURN_IF_NULL(handle, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(handle->funcs->set_batch_command, HAL_HDCP_ERROR_NOT_IMPLEMENTED); + HAL_HDCP_RETURN_IF_NULL(batch_command, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(batch_command->custom.name, HAL_HDCP_ERROR_INVALID_PARAMETER); + HAL_HDCP_RETURN_IF_NULL(batch_command->custom.value, HAL_HDCP_ERROR_INVALID_PARAMETER); + /* error_command could be NULL. */ + + AUTOCLEAN_LOCKER(&handle->lock); + + HAL_HDCP_CHECK_STATE(handle->state, HAL_HDCP_STATE_INITIALIZED); + + return handle->funcs->set_batch_command(handle->backend, batch_command, error_command); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a7ed9f9 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +SET(HAL_HDCP_TEST "hdcp-haltests") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Werror") + +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/ HALTEST_SRCS) +ADD_EXECUTABLE(${HAL_HDCP_TEST} ${HALTEST_SRCS}) + +INCLUDE(FindPkgConfig) +pkg_check_modules(${HAL_HDCP_TEST} REQUIRED glib-2.0 gmock capi-system-info) + +FOREACH(flag ${${HAL_HDCP_TEST}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -fPIC -pie -Wall") + +TARGET_LINK_LIBRARIES(${HAL_HDCP_TEST} ${PROJECT_NAME} ${${HAL_HDCP_TEST}_LDFLAGS}) +SET_TARGET_PROPERTIES(${HAL_HDCP_TEST} PROPERTIES COMPILE_FLAGS "-fPIE") +SET_TARGET_PROPERTIES(${HAL_HDCP_TEST} PROPERTIES LINK_FLAGS "-pie") + +INSTALL(TARGETS ${HAL_HDCP_TEST} DESTINATION ${EXEC_PREFIX}/hal/) diff --git a/tests/hdcp_hal_test.cpp b/tests/hdcp_hal_test.cpp new file mode 100644 index 0000000..fb5ab44 --- /dev/null +++ b/tests/hdcp_hal_test.cpp @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haesu Gwon + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +static int gRet; +static void *gHalHandle; +static const char *gSocketIp = "192.168.0.1"; +static int gPortIp = 100; + +/* + * main class + */ +class HdcpHalTest : public testing::Test +{ + public: + virtual void SetUp() + { + gRet = hal_hdcp_init(&gHalHandle); + if (gRet == HAL_HDCP_ERROR_NONE) { + std::cout << "HDCP HAL init - handle: " << gHalHandle << std::endl; + return; + } + + std::cout << "HDCP HAL init failed " << gRet << std::endl; + } + + virtual void TearDown() + { + if (gHalHandle) { + std::cout << "HDCP HAL deinit - handle: " << gHalHandle << std::endl; + gRet = hal_hdcp_deinit(gHalHandle); + gHalHandle = nullptr; + } + } +}; + +/** + * @testcase InitP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Initialize HDCP HAL handle + * @apicovered hal_hdcp_init + * @passcase when hal_hdcp_init returns HAL_HDCP_ERROR_NONE and the handle "gHalHandle" is not a null pointer + * @failcase when handle "gHalHandle" is a null pointer + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, InitP) +{ + ASSERT_NE(gHalHandle, nullptr); +} + +/** + * @testcase InitN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Initialize HDCP HAL handle + * @apicovered hal_hdcp_init + * @passcase when input handle of hal_hdcp_init is a null pointer and hal_hdcp_init returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_init does not returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, InitN) +{ + gRet = hal_hdcp_init(nullptr); + EXPECT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); +} + +/** + * @testcase DeinitP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Deinitialize HDCP HAL handle + * @apicovered hal_hdcp_init, hal_hdcp_deinit + * @passcase when hal_hdcp_deinit returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_deinit does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, DeinitP) +{ + void *hal_handle = nullptr; + + gRet = hal_hdcp_init(&hal_handle); + + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + ASSERT_NE(hal_handle, nullptr); + + if (hal_handle) { + gRet = hal_hdcp_deinit(hal_handle); + EXPECT_EQ(gRet, HAL_HDCP_ERROR_NONE); + hal_handle = nullptr; + } +} + +/** + * @testcase DeinitN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Deinitialize HDCP HAL handle + * @apicovered hal_hdcp_init, hal_hdcp_deinit + * @passcase when hal_hdcp_deinit returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_deinit does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, DeinitN) +{ + gRet = hal_hdcp_deinit(nullptr); + EXPECT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); +} + +/** + * @testcase HdcpSetCommandP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Set command + * @apicovered hal_hdcp_set_batch_command + * @passcase when hal_hdcp_set_batch_command returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_set_batch_command does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpSetBatchCommandP) +{ + hal_hdcp_batch_command_control_s batch_command; + batch_command.command_set_flag |= HAL_HDCP_COMMAND_CUSTOM; + batch_command.custom.name = "custom_command_name"; + batch_command.custom.value = (void*)10; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_set_batch_command(gHalHandle, &batch_command, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpSetBatchCommandN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Set command + * @apicovered hal_hdcp_set_batch_command + * @passcase when hal_hdcp_set_batch_command returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_set_batch_command does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpSetBatchCommandN) +{ + hal_hdcp_batch_command_control_s batch_command; + batch_command.command_set_flag |= HAL_HDCP_COMMAND_CUSTOM; + batch_command.custom.name = "custom_command_name"; + batch_command.custom.value = (void*)10; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_set_batch_command(nullptr, &batch_command, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_set_batch_command(gHalHandle, nullptr, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpOpenP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Open HDCP + * @apicovered hal_hdcp_open + * @passcase when hal_hdcp_open returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_open does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpOpenP) +{ + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpCloseP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Close HDCP + * @apicovered hal_hdcp_close + * @passcase when hal_hdcp_close returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_close does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpCloseP) +{ + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpCloseN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Close HDCP + * @apicovered hal_hdcp_close + * @passcase when hal_hdcp_open is not called and hal_hdcp_close returns HAL_HDCP_ERROR_INVALID_STATE + * @failcase when hal_hdcp_close does not return HAL_HDCP_ERROR_INVALID_STATE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpCloseN1) +{ + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_STATE); +} + +/** + * @testcase HdcpCloseN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Close HDCP + * @apicovered hal_hdcp_close + * @passcase when hal_hdcp_close returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_close does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpCloseN2) +{ + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); +} + +/** + * @testcase HdcpStartReceiverP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Start HDCP receiver + * @apicovered hal_hdcp_start_receiver + * @passcase when hal_hdcp_start_receiver returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_start_receiver does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStartReceiverP) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_receiver(gHalHandle, 100, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStartReceiverN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Start HDCP receiver + * @apicovered hal_hdcp_start_receiver + * @passcase when hal_hdcp_start_receiver returns HAL_HDCP_ERROR_INVALID_STATE + * @failcase when hal_hdcp_start_receiver does not return HAL_HDCP_ERROR_INVALID_STATE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStartReceiverN1) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_start_receiver(gHalHandle, 100, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_STATE); +} + +/** + * @testcase HdcpStartReceiverN2 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Start HDCP receiver + * @apicovered hal_hdcp_start_receiver + * @passcase when hal_hdcp_start_receiver returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_start_receiver does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStartReceiverN2) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_receiver(nullptr, 100, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_start_receiver(gHalHandle, 100, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStopReceiverP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Stop HDCP receiver + * @apicovered hal_hdcp_stop_receiver + * @passcase when hal_hdcp_stop_receiver returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_stop_receiver does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStopReceiverP) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_receiver(gHalHandle, 100, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_receiver(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStopReceiverN1 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Stop HDCP receiver + * @apicovered hal_hdcp_stop_receiver + * @passcase when hal_hdcp_stop_receiver returns HAL_HDCP_ERROR_INVALID_STATE + * @failcase when hal_hdcp_stop_receiver does not return HAL_HDCP_ERROR_INVALID_STATE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStopReceiverN1) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_stop_receiver(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_STATE); +} + +/** + * @testcase HdcpStopReceiverN2 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Stop HDCP receiver + * @apicovered hal_hdcp_stop_receiver + * @passcase when hal_hdcp_stop_receiver returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_stop_receiver does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStopReceiverN2) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_receiver(gHalHandle, 100, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_receiver(nullptr, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStartTransmitterP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Start HDCP transmitter + * @apicovered hal_hdcp_start_transmitter + * @passcase when hal_hdcp_start_transmitter returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_start_transmitter does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStartTransmitterP) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStartTransmitterN1 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Start HDCP transmitter + * @apicovered hal_hdcp_start_transmitter + * @passcase when hal_hdcp_start_transmitter returns HAL_HDCP_ERROR_INVALID_STATE + * @failcase when hal_hdcp_start_transmitter does not return HAL_HDCP_ERROR_INVALID_STATE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStartTransmitterN1) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_STATE); +} + +/** + * @testcase HdcpStartTransmitterN2 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Start HDCP transmitter + * @apicovered hal_hdcp_start_transmitter + * @passcase when hal_hdcp_start_transmitter returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_start_transmitter does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStartTransmitterN2) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(nullptr, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStopTransmitterP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Stop HDCP transmitter + * @apicovered hal_hdcp_stop_transmitter + * @passcase when hal_hdcp_stop_transmitter returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_stop_transmitter does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStopTransmitterP) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpStopTransmitterN1 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Stop HDCP transmitter + * @apicovered hal_hdcp_stop_transmitter + * @passcase when hal_hdcp_stop_transmitter returns HAL_HDCP_ERROR_INVALID_STATE + * @failcase when hal_hdcp_stop_transmitter does not return HAL_HDCP_ERROR_INVALID_STATE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStopTransmitterN1) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_STATE); +} + +/** + * @testcase HdcpStopTransmitterN2 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Stop HDCP transmitter + * @apicovered hal_hdcp_stop_transmitter + * @passcase when hal_hdcp_stop_transmitter returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_stop_transmitter does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpStopTransmitterN2) +{ + uint32_t hdcp_id = 0; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_transmitter(nullptr, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpAllocateBufferP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Allocate output buffer + * @apicovered hal_hdcp_allocate_output_buffer + * @passcase when hal_hdcp_allocate_output_buffer returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_allocate_output_buffer does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpAllocateBufferP) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s output; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_allocate_output_buffer(gHalHandle, 1000, false, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpAllocateBufferN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Allocate output buffer + * @apicovered hal_hdcp_allocate_output_buffer + * @passcase when hal_hdcp_allocate_output_buffer returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_allocate_output_buffer does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpAllocateBufferN) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s output; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_allocate_output_buffer(nullptr, 1000, false, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_allocate_output_buffer(gHalHandle, 1000, false, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpReleaseBufferP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Release output buffer + * @apicovered hal_hdcp_release_output_buffer + * @passcase when hal_hdcp_release_output_buffer returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_release_output_buffer does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpReleaseBufferP) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s output; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_allocate_output_buffer(gHalHandle, 1000, false, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_release_output_buffer(gHalHandle, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpReleaseBufferN + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Release output buffer + * @apicovered hal_hdcp_release_output_buffer + * @passcase when hal_hdcp_release_output_buffer returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_release_output_buffer does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpReleaseBufferN) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s output; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_allocate_output_buffer(gHalHandle, 1000, false, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_release_output_buffer(nullptr, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_release_output_buffer(gHalHandle, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpDecryptP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Decrypt HDCP data + * @apicovered hal_hdcp_decrypt + * @passcase when hal_hdcp_decrypt returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_decrypt does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpDecryptP) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s input, output; + uint32_t decrypt_info; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_allocate_output_buffer(gHalHandle, 1000, false, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + input.size = 100; + gRet = hal_hdcp_decrypt(gHalHandle, &input, (void *)&decrypt_info, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpDecryptN1 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Decrypt HDCP data + * @apicovered hal_hdcp_decrypt + * @passcase when hal_hdcp_decrypt returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_decrypt does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpDecryptN1) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s output; + int decrypt_info; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_decrypt(gHalHandle, nullptr, (void *)&decrypt_info, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpEncryptP + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Positive, Encrypt HDCP data + * @apicovered hal_hdcp_encrypt + * @passcase when hal_hdcp_encrypt returns HAL_HDCP_ERROR_NONE + * @failcase when hal_hdcp_encrypt does not return HAL_HDCP_ERROR_NONE + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpEncryptP) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s input, output; + uint32_t encrypt_info; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_allocate_output_buffer(gHalHandle, 1000, false, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + input.size = 100; + gRet = hal_hdcp_encrypt(gHalHandle, &input, hdcp_id, (void *)&encrypt_info, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +/** + * @testcase HdcpEncryptN1 + * @since HAL_MODULE_HDCP 1.0 + * @author SR(haesu.gwon) + * @reviewer SR(gilbok) + * @type auto + * @description Negative, Encrypt HDCP data + * @apicovered hal_hdcp_encrypt + * @passcase when hal_hdcp_encrypt returns HAL_HDCP_ERROR_INVALID_PARAMETER + * @failcase when hal_hdcp_encrypt does not return HAL_HDCP_ERROR_INVALID_PARAMETER + * @precondition None + * @postcondition None + */ +TEST_F(HdcpHalTest, HdcpEncryptN1) +{ + uint32_t hdcp_id = 0; + hal_hdcp_buffer_s input, output; + uint32_t encrypt_info; + input.size = 100; + + gRet = hal_hdcp_open(gHalHandle, HAL_HDCP_DEVICE_RECEIVER, HAL_HDCP_VERSION_2_0); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_start_transmitter(gHalHandle, gSocketIp, gPortIp, &hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_encrypt(gHalHandle, nullptr, hdcp_id, (void *)&encrypt_info, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_encrypt(gHalHandle, &input, hdcp_id, nullptr, &output); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_encrypt(gHalHandle, &input, hdcp_id, (void *)&encrypt_info, nullptr); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_INVALID_PARAMETER); + + gRet = hal_hdcp_stop_transmitter(gHalHandle, hdcp_id); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); + + gRet = hal_hdcp_close(gHalHandle); + ASSERT_EQ(gRet, HAL_HDCP_ERROR_NONE); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} -- 2.34.1