From 29104708fa8abe6f55b83da37702b754486918af Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 25 Jul 2012 13:04:12 +0200 Subject: [PATCH] added support for the Raspberry Pi. this needs a recent firmware for the Pi. libCEC's configure script will check whether the version is correct --- ChangeLog | 1 + README | 25 ++ configure.ac | 102 +++++ debian/changelog | 1 + project/RPi/bootstrap.sh | 32 ++ project/RPi/build-deps.sh | 36 ++ project/RPi/build.sh | 37 ++ project/RPi/config | 37 ++ project/RPi/get-toolchain.sh | 29 ++ src/lib/Makefile.am | 7 + src/lib/adapter/AdapterFactory.cpp | 26 +- .../RPi/RPiCECAdapterCommunication.cpp | 422 ++++++++++++++++++ .../adapter/RPi/RPiCECAdapterCommunication.h | 107 +++++ .../adapter/RPi/RPiCECAdapterDetection.cpp | 59 +++ src/lib/adapter/RPi/RPiCECAdapterDetection.h | 41 ++ .../adapter/RPi/RPiCECAdapterMessageQueue.cpp | 209 +++++++++ .../adapter/RPi/RPiCECAdapterMessageQueue.h | 117 +++++ 17 files changed, 1286 insertions(+), 2 deletions(-) create mode 100755 project/RPi/bootstrap.sh create mode 100755 project/RPi/build-deps.sh create mode 100755 project/RPi/build.sh create mode 100644 project/RPi/config create mode 100755 project/RPi/get-toolchain.sh create mode 100644 src/lib/adapter/RPi/RPiCECAdapterCommunication.cpp create mode 100644 src/lib/adapter/RPi/RPiCECAdapterCommunication.h create mode 100644 src/lib/adapter/RPi/RPiCECAdapterDetection.cpp create mode 100644 src/lib/adapter/RPi/RPiCECAdapterDetection.h create mode 100644 src/lib/adapter/RPi/RPiCECAdapterMessageQueue.cpp create mode 100644 src/lib/adapter/RPi/RPiCECAdapterMessageQueue.h diff --git a/ChangeLog b/ChangeLog index fb77809..12e2792 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ libcec (1.8.1-1) unstable; urgency=low * changed/added: + * added support for the Raspberry Pi. * added cec-client -i / cec-client --info that calls GetLibInfo() * header cleanups * added CAdapterFactory, to create IAdapterCommunication instances diff --git a/README b/README index c8b2aef..5c7364b 100644 --- a/README +++ b/README @@ -74,6 +74,31 @@ you will need to manually build libcec with Visual C++ 2010 and then build LibCecSharp.sln with Visual Studio 2008. See create-installer.bat for the required steps. +=============================================================================== + === Raspberry Pi === +=============================================================================== + +We've included a script that builds libCEC with support for the Raspberry Pi +in /project/RPi/build.sh. This script pulls in the latest toolchain and +firmware, builds the necessary dependencies, and finally builds libCEC. The +result will be a softfp build. + +To build libCEc on your development machine, follow these instructions: +* run '/project/RPi/build.sh /desired/destination/path/for/binaries' +* run 'make install' + +To build libCEC on the Pi itself, just follow the instructions for Linux. +The configure script automatically checks whether the required headers and +libraries can be found. + +To specify the path of the Raspberry Pi's development headers, use the +following option for 'configure': +--with-rpi-include-path="/path/to/opt/vc/include" + +To specify the path of the Raspberry Pi's libraries, use the following option +for 'configure': +--with-rpi-lib-path="/path/to/libbcm_host.so" + =============================================================================== === Debugging / Testing === =============================================================================== diff --git a/configure.ac b/configure.ac index 1ae91b0..ae1b5a5 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,9 @@ msg_dl_missing="required library 'dl' is missing" msg_udev_missing="library 'udev' is missing - adapter detection will not be available" msg_dirent_missing="dirent.h header is missing - adapter detection will not be available" msg_lockdev_missing="required library 'liblockdev' is missing" +msg_rpi_api_missing="Raspberry Pi API not found or incompatible with libCEC" +msg_rpi_will_check="will check for RPi support" +msg_rpi_unsupported_target="will not check for RPi support (unsupported cpu: ${host_cpu})" msg_required_header_missing="required header is missing" ## debugging symbols @@ -40,6 +43,42 @@ AC_ARG_ENABLE([optimisation], [use_optimisation=$enableval], [use_optimisation=yes]) +## Raspberry Pi support +AC_ARG_ENABLE([rpi], + [AS_HELP_STRING([--enable-rpi], + [enable support for the Raspberry Pi (default is auto)])], + [use_rpi=$enableval], + [use_rpi=auto]) + +## Optional path to the RPi's dev headers +AC_ARG_WITH([rpi-include-path], + [AS_HELP_STRING([--with-rpi-include-path], + [location of the Raspberry Pi headers (location of /opt/vc/include, default is auto)])], + [RPI_CFLAGS="-I$withval -I$withval/interface/vcos/pthreads"]) + +## Optional path to libbcm_host.so +AC_ARG_WITH([rpi-lib-path], + [AS_HELP_STRING([--with-rpi-lib-path], + [location of the Raspberry Pi libraries (location of libbcm_host.so, default is auto)])], + [RPI_LIBS="-L$withval"]) + +## only check for the RPi API on ARM targets +if test "x$use_rpi" != "xno"; then + case "${host_cpu}" in + arm*) + AC_MSG_NOTICE($msg_rpi_will_check) + ;; + *) + if test "x$use_rpi" = "xyes"; then + AC_MSG_ERROR($msg_rpi_unsupported_target) + else + AC_MSG_NOTICE($msg_rpi_unsupported_target) + fi + use_rpi="no" + ;; + esac +fi + ## add the top dir and include to the include path, so we can include config.h and cec.h CPPFLAGS="$CPPFLAGS -I\$(abs_top_srcdir)/src -I\$(abs_top_srcdir)/include" @@ -98,6 +137,58 @@ case "${host}" in AC_CHECK_HEADER(time.h,,AC_MSG_ERROR($msg_required_header_missing)) AC_CHECK_HEADER(sys/prctl.h,,AC_MSG_ERROR($msg_required_header_missing)) + + ## search for the RPi API. we need to check a couple of things to see if + ## it's recent enough and contains the calls needed for libCEC to operate + ## correctly. + if test "x$use_rpi" != "xno"; then + CPPFLAGS="$CPPFLAGS $RPI_CFLAGS" + libs_pre_rpi="$LIBS" + LIBS="$LIBS $RPI_LIBS -lvcos -lvchiq_arm" + + check_rpi_cec_service="yes" + + ## check for headers we need + AC_CHECK_HEADER(interface/vmcs_host/vc_cec.h,,check_rpi_cec_service="no") + AC_CHECK_HEADER(interface/vmcs_host/vc_cecservice.h,,check_rpi_cec_service="no") + AC_CHECK_HEADER(interface/vchiq_arm/vchiq_if.h,,check_rpi_cec_service="no") + AC_CHECK_HEADER(bcm_host.h,,check_rpi_cec_service="no") + + ## check if the headers contain support for libCEC. + ## VC_CECSERVICE_VER needs to be defined + AC_MSG_CHECKING([interface/vmcs_host/vc_cec.h compatibility]) + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +#include +#if !defined(VC_CECSERVICE_VER) +#error RPi headers does not contain libCEC support +#endif]], [[]])],[AC_MSG_RESULT([yes])],[check_rpi_cec_service="no"; AC_MSG_RESULT([no])]) + + ## check if the methods we're using can be found in libbcm_host.so, so we don't use an incompatible version + AC_CHECK_LIB(bcm_host,vchi_initialise,,check_rpi_cec_service="no") + libs_tmp="$LIBS" + AC_CHECK_LIB(bcm_host,vc_vchi_cec_init,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_get_logical_address,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_get_physical_address,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_param2message,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_poll_address,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_register_callback,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_release_logical_address,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vc_cec_set_passive,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vcos_init,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vchiq_initialise,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vchi_initialise,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,vchi_create_connection,,check_rpi_cec_service="no") + AC_CHECK_LIB(bcm_host,bcm_host_init,,check_rpi_cec_service="no") + LIBS="$libs_tmp" + + if test "x$check_rpi_cec_service" != "xyes" && test "x$use_rpi" = "xyes"; then + AC_MSG_ERROR($msg_rpi_api_missing) + elif test "x$check_rpi_cec_service" != "xyes"; then + use_rpi="no" + LIBS="$libs_pre_rpi" + fi + fi ;; *-apple-darwin*) AC_CHECK_HEADER(mach/mach_time.h,,AC_MSG_ERROR($msg_required_header_missing)) @@ -133,6 +224,17 @@ else features="$features\n Pulse-Eight CEC Adapter detection :\tno" fi +## mark RPi support as available if the required headers and libs were found +if test "x$use_rpi" != "xno"; then + AC_DEFINE([HAVE_RPI_API],[1],[Define to 1 to include RPi support]) + AM_CONDITIONAL(USE_RPI_API, true) + features="$features\n Raspberry Pi support :\t\tyes" + LIB_INFO="$LIB_INFO 'RPi'" +else + AM_CONDITIONAL(USE_RPI_API, false) + features="$features\n Raspberry Pi support :\t\tno" +fi + ## check if our build system is complete AC_CHECK_HEADER(algorithm,,AC_MSG_ERROR($msg_required_header_missing)) AC_CHECK_HEADER(ctype.h,,AC_MSG_ERROR($msg_required_header_missing)) diff --git a/debian/changelog b/debian/changelog index fb77809..12e2792 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ libcec (1.8.1-1) unstable; urgency=low * changed/added: + * added support for the Raspberry Pi. * added cec-client -i / cec-client --info that calls GetLibInfo() * header cleanups * added CAdapterFactory, to create IAdapterCommunication instances diff --git a/project/RPi/bootstrap.sh b/project/RPi/bootstrap.sh new file mode 100755 index 0000000..83f184c --- /dev/null +++ b/project/RPi/bootstrap.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +_usage() +{ + echo "Usage: $0 /path/to/toolchain /source/path" + exit 1 +} + +if [[ -z "$2" || ! -d "$1" || ! -d "$2" ]]; then + echo "1 = '$1'" + echo "2 = '$2'" + _usage +fi + +SCRIPT_PATH=`dirname $0` +cd $SCRIPT_PATH +SCRIPT_PATH=`pwd` +cd - + +source $SCRIPT_PATH/config +mkdir -p $SCRIPT_PATH/deps + +cd "$2" + +if [ -f "configure.ac" ]; then + _set_toolchain_path "$1" + autoreconf -vif + exit $? +fi + +exit 0 + diff --git a/project/RPi/build-deps.sh b/project/RPi/build-deps.sh new file mode 100755 index 0000000..be6b22a --- /dev/null +++ b/project/RPi/build-deps.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +SCRIPT_PATH=`dirname $0` +cd $SCRIPT_PATH +SCRIPT_PATH=`pwd` +cd - + +source $SCRIPT_PATH/config + +mkdir -p $SCRIPT_PATH/deps/build +cd $SCRIPT_PATH/deps/build + +if [ ! -d lockdev ]; then + wget ${TARBALL_LOCATION}${LOCKDEV_TARBALL} + tar -Jxf $LOCKDEV_TARBALL + + mv `echo $LOCKDEV_TARBALL | sed 's/.tar.xz//'` lockdev + rm $LOCKDEV_TARBALL + + cd $SCRIPT_PATH/deps/build/lockdev && \ + $SCRIPT_PATH/bootstrap.sh $SCRIPT_PATH/toolchain . + + if [ $? -eq 0 ]; then + _set_toolchain_path "$SCRIPT_PATH/toolchain" + ./configure --host=$TARGET_HOST --build=`cc -dumpmachine` --prefix=$SCRIPT_PATH/deps && \ + make && \ + make install + + exit $? + else + exit 1 + fi +fi + +exit 0 + diff --git a/project/RPi/build.sh b/project/RPi/build.sh new file mode 100755 index 0000000..1d684e1 --- /dev/null +++ b/project/RPi/build.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +SCRIPT_PATH=`dirname $0` +cd $SCRIPT_PATH +SCRIPT_PATH=`pwd` +cd - + +if [ -z "$1" ]; then + DEST_DIR="/opt/libcec-rpi" +else + DEST_DIR="$1" +fi + +source $SCRIPT_PATH/config + +$SCRIPT_PATH/get-toolchain.sh && \ +$SCRIPT_PATH/build-deps.sh && \ +$SCRIPT_PATH/bootstrap.sh $SCRIPT_PATH/toolchain . + +if [ $? -eq 0 ]; then + _set_toolchain_path "$SCRIPT_PATH/toolchain" + # configure with --enable-rpi-cec-api so we bug out if we can't find Pi support or can't build it + ./configure --host=$TARGET_HOST \ + --build=`cc -dumpmachine` \ + --prefix=$DEST_DIR \ + --enable-debug \ + --enable-rpi \ + --with-rpi-include-path="${SCRIPT_PATH}/firmware/hardfp/opt/vc/include" \ + --with-rpi-lib-path="${SCRIPT_PATH}/firmware/hardfp/opt/vc/lib" && \ + make clean && \ + make V=0 + + exit $? +else + exit 1 +fi + diff --git a/project/RPi/config b/project/RPi/config new file mode 100644 index 0000000..b81021d --- /dev/null +++ b/project/RPi/config @@ -0,0 +1,37 @@ +TOOLCHAIN_GIT="git://github.com/raspberrypi/tools.git" +FIRMWARE_GIT="git://github.com/raspberrypi/firmware.git" +TARBALL_LOCATION="http://sources.openelec.tv/devel/" +LOCKDEV_TARBALL="lockdev-16b8996.tar.xz" + +_set_toolchain_path() +{ + echo "Setting toolchain path to: '$1'" + + export TARGET_TOOLCHAIN_PATH="$1/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin" + export TARGET_TOOLCHAIN_LIB_PATH="$1/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/lib" + export TARGET_HOST="arm-bcm2708hardfp-linux-gnueabi" + + export CC=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-gcc + export CXX=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-g++ + export LD=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-ld + export AS=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-as + export AR=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-ar + export NM=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-nm + export RANLIB=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-ranlib + export OBJCOPY=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-objcopy + export OBJDUMP=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-objdump + export STRIP=${TARGET_TOOLCHAIN_PATH}/${TARGET_HOST}-strip + + export CPPFLAGS="-I${SCRIPT_PATH}/deps/include" + export CFLAGS="-march=armv6 -mfpu=vfp -mfloat-abi=hard -Wno-psabi -Wa,-mno-warn-deprecated -O3 -fexcess-precision=fast -ffast-math -I${SCRIPT_PATH}/deps/include" + export CXXFLAGS="$CFLAGS" + export LDFLAGS="-march=armv6 -mtune=arm1176jzf-s -L${SCRIPT_PATH}/deps/lib" + + #export PKG_CONFIG_PATH="$TARGET_PKG_CONFIG_PATH" + export PKG_CONFIG_LIBDIR="${SCRIPT_PATH}/deps/lib/pkgconfig:${SCRIPT_PATH}/deps/lib/share/pkgconfig" + export PKG_CONFIG_SYSROOT_DIR="${SCRIPT_PATH}/deps" + + export PATH="$TARGET_TOOLCHAIN_PATH:$PATH" + export LD_LIBRARY_PATH="$TARGET_TOOLCHAIN_LIB_PATH:$LD_LIBRARY_PATH" +} + diff --git a/project/RPi/get-toolchain.sh b/project/RPi/get-toolchain.sh new file mode 100755 index 0000000..96e3634 --- /dev/null +++ b/project/RPi/get-toolchain.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +SCRIPT_PATH=`dirname $0` +cd $SCRIPT_PATH +SCRIPT_PATH=`pwd` +cd - + +source $SCRIPT_PATH/config + +if [ ! -d $SCRIPT_PATH/toolchain ]; then + git clone $TOOLCHAIN_GIT $SCRIPT_PATH/toolchain +else + cd $SCRIPT_PATH/toolchain +# git pull +fi + +if [ ! -d $SCRIPT_PATH/firmware ]; then + git clone $FIRMWARE_GIT $SCRIPT_PATH/firmware +else + cd $SCRIPT_PATH/firmware +# git pull +fi + +if [[ -d $SCRIPT_PATH/toolchain && -d $SCRIPT_PATH/firmware ]]; then + exit 0 +else + exit 1 +fi + diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index d4340ec..7ea1e99 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -43,4 +43,11 @@ libcec_la_SOURCES += adapter/Pulse-Eight/USBCECAdapterMessage.cpp \ platform/nvidia/nv-edid.cpp endif +## Raspberry Pi support +if USE_RPI_API +libcec_la_SOURCES += adapter/RPi/RPiCECAdapterDetection.cpp \ + adapter/RPi/RPiCECAdapterCommunication.cpp \ + adapter/RPi/RPiCECAdapterMessageQueue.cpp +endif + libcec_la_LDFLAGS = @LIBS_LIBCEC@ -version-info @VERSION@ diff --git a/src/lib/adapter/AdapterFactory.cpp b/src/lib/adapter/AdapterFactory.cpp index ec216ab..9ee9cc7 100644 --- a/src/lib/adapter/AdapterFactory.cpp +++ b/src/lib/adapter/AdapterFactory.cpp @@ -42,6 +42,11 @@ #include "Pulse-Eight/USBCECAdapterCommunication.h" #endif +#if defined(HAVE_RPI_API) +#include "RPi/RPiCECAdapterDetection.h" +#include "RPi/RPiCECAdapterCommunication.h" +#endif + using namespace std; using namespace CEC; @@ -61,7 +66,16 @@ int8_t CAdapterFactory::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, m_lib->AddLog(CEC_LOG_WARNING, "libCEC has not been compiled with support for the Pulse-Eight USB-CEC Adapter"); #endif -#if !defined(HAVE_P8_USB) +#if defined(HAVE_RPI_API) + if (iAdaptersFound < iBufSize && CRPiCECAdapterDetection::FindAdapter() && + (!strDevicePath || !strcmp(strDevicePath, CEC_RPI_VIRTUAL_COM))) + { + snprintf(deviceList[iAdaptersFound].path, 1024, CEC_RPI_VIRTUAL_PATH); + snprintf(deviceList[iAdaptersFound++].comm, 1024, CEC_RPI_VIRTUAL_COM); + } +#endif + +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) #error "libCEC doesn't have support for any type of adapter. please check your build system or configuration" #endif @@ -70,15 +84,23 @@ int8_t CAdapterFactory::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_t iBaudRate) { +#if defined(HAVE_RPI_API) + if (!strcmp(strPort, CEC_RPI_VIRTUAL_COM)) + return new CRPiCECAdapterCommunication(m_lib->m_cec); +#endif + #if defined(HAVE_P8_USB) return new CUSBCECAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); #endif -#if !defined(HAVE_P8_USB) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) return NULL; #endif } void CAdapterFactory::InitVideoStandalone(void) { +#if defined(HAVE_RPI_API) + CRPiCECAdapterCommunication::InitHost(); +#endif } diff --git a/src/lib/adapter/RPi/RPiCECAdapterCommunication.cpp b/src/lib/adapter/RPi/RPiCECAdapterCommunication.cpp new file mode 100644 index 0000000..5e1efd4 --- /dev/null +++ b/src/lib/adapter/RPi/RPiCECAdapterCommunication.cpp @@ -0,0 +1,422 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" + +#if defined(HAVE_RPI_API) +#include "RPiCECAdapterCommunication.h" + +extern "C" { +#include +} + +#include "lib/CECTypeUtils.h" +#include "lib/LibCEC.h" +#include "lib/platform/util/StdString.h" +#include "RPiCECAdapterMessageQueue.h" + +using namespace CEC; +using namespace PLATFORM; + +#define LIB_CEC m_callback->GetLib() + +static bool g_bHostInited = false; + +// callback for the RPi CEC service +void rpi_cec_callback(void *callback_data, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4) +{ + if (callback_data) + static_cast(callback_data)->OnDataReceived(p0, p1, p2, p3, p4); +} + +CRPiCECAdapterCommunication::CRPiCECAdapterCommunication(IAdapterCommunicationCallback *callback) : + IAdapterCommunication(callback), + m_logicalAddress(CECDEVICE_UNKNOWN), + m_bLogicalAddressChanged(false) +{ + m_queue = new CRPiCECAdapterMessageQueue(this); +} + +CRPiCECAdapterCommunication::~CRPiCECAdapterCommunication(void) +{ + delete(m_queue); + Close(); +} + +const char *ToString(const VC_CEC_ERROR_T error) +{ + switch(error) + { + case VC_CEC_SUCCESS: + return "success"; + case VC_CEC_ERROR_NO_ACK: + return "no ack"; + case VC_CEC_ERROR_SHUTDOWN: + return "shutdown"; + case VC_CEC_ERROR_BUSY: + return "device is busy"; + case VC_CEC_ERROR_NO_LA: + return "no logical address"; + case VC_CEC_ERROR_NO_PA: + return "no physical address"; + case VC_CEC_ERROR_NO_TOPO: + return "no topology"; + case VC_CEC_ERROR_INVALID_FOLLOWER: + return "invalid follower"; + case VC_CEC_ERROR_INVALID_ARGUMENT: + return "invalid arg"; + default: + return "unknown"; + } +} + +bool CRPiCECAdapterCommunication::IsInitialised(void) +{ + CLockObject lock(m_mutex); + return m_bInitialised; +} + +void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) +{ + VC_CEC_NOTIFY_T reason = (VC_CEC_NOTIFY_T)CEC_CB_REASON(header); + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "received data: header:%08X p0:%08X p1:%08X p2:%08X p3:%08X reason:%x", header, p0, p1, p2, p3, reason); + + switch (reason) + { + case VC_CEC_RX: + // CEC data received + { + // translate into a VC_CEC_MESSAGE_T + VC_CEC_MESSAGE_T message; + vc_cec_param2message(header, p0, p1, p2, p3, &message); + + // translate to a cec_command + cec_command command; + cec_command::Format(command, + (cec_logical_address)message.initiator, + (cec_logical_address)message.follower, + (cec_opcode)CEC_CB_OPCODE(p0)); + + // copy parameters + for (uint8_t iPtr = 1; iPtr < message.length; iPtr++) + command.PushBack(message.payload[iPtr]); + + // send to libCEC + m_callback->OnCommandReceived(command); + } + break; + case VC_CEC_TX: + { + // handle response to a command that was sent earlier + m_queue->MessageReceived((cec_opcode)CEC_CB_OPCODE(p0), (cec_logical_address)CEC_CB_INITIATOR(p0), (cec_logical_address)CEC_CB_FOLLOWER(p0), CEC_CB_RC(header)); + } + break; + case VC_CEC_BUTTON_PRESSED: + { + // translate into a cec_command + cec_command command; + cec_command::Format(command, (cec_logical_address)CEC_CB_INITIATOR(p0), (cec_logical_address)CEC_CB_FOLLOWER(p0), CEC_OPCODE_USER_CONTROL_PRESSED); + command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0)); + + // send to libCEC + m_callback->OnCommandReceived(command); + } + break; + case VC_CEC_BUTTON_RELEASE: + { + // translate into a cec_command + cec_command command; + cec_command::Format(command, (cec_logical_address)CEC_CB_INITIATOR(p0), (cec_logical_address)CEC_CB_FOLLOWER(p0), CEC_OPCODE_USER_CONTROL_RELEASE); + command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0)); + + // send to libCEC + m_callback->OnCommandReceived(command); + } + break; + case VC_CEC_LOGICAL_ADDR: + { + CLockObject lock(m_mutex); + if (CEC_CB_RC(header) == VCHIQ_SUCCESS) + { + m_bLogicalAddressChanged = true; + m_logicalAddress = (cec_logical_address)(p0 & 0xF); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address changed to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress); + } + else + { + m_logicalAddress = CECDEVICE_BROADCAST; + LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to change the logical address, reset to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress); + } + m_logicalAddressCondition.Signal(); + } + break; + case VC_CEC_TOPOLOGY: + case VC_CEC_REMOTE_PRESSED: + case VC_CEC_REMOTE_RELEASE: + break; + default: + LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignoring unknown reason %x", reason); + break; + } +} + +int CRPiCECAdapterCommunication::InitHostCEC(void) +{ + VCHIQ_INSTANCE_T vchiq_instance; + int iResult; + + if ((iResult = vchiq_initialise(&vchiq_instance)) != VCHIQ_SUCCESS) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult); + CStdString strError; + strError.Format("%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult); + m_strError = strError; + return iResult; + } + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchiq_initialise succeeded", __FUNCTION__); + + if ((iResult = vchi_initialise(&m_vchi_instance)) != VCHIQ_SUCCESS) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_initialise failed (%d)", __FUNCTION__, iResult); + CStdString strError; + strError.Format("%s - vchi_initialise failed (%d)", __FUNCTION__, iResult); + m_strError = strError; + return iResult; + } + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_initialise succeeded", __FUNCTION__); + + vchiq_instance = (VCHIQ_INSTANCE_T)m_vchi_instance; + + m_vchi_connection = vchi_create_connection(single_get_func_table(), + vchi_mphi_message_driver_func_table()); + + if ((iResult = vchi_connect(&m_vchi_connection, 1, m_vchi_instance)) != VCHIQ_SUCCESS) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_connect failed (%d)", __FUNCTION__, iResult); + CStdString strError; + strError.Format("%s - vchi_connect failed (%d)", __FUNCTION__, iResult); + m_strError = strError; + return iResult; + } + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_connect succeeded", __FUNCTION__); + + return VCHIQ_SUCCESS; +} + +bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks) /* = false */, bool bStartListening) +{ + Close(); + + if (InitHostCEC() != VCHIQ_SUCCESS) + return false; + + if (bStartListening) + { + // enable passive mode + vc_cec_set_passive(true); + + // register the callback + vc_cec_register_callback(((CECSERVICE_CALLBACK_T)rpi_cec_callback), (void*)this); + + // release previous LA + vc_cec_release_logical_address(); + if (!m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged, iTimeoutMs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to release the previous LA"); + return false; + } + + // register LA "freeuse" + if (RegisterLogicalAddress(CECDEVICE_FREEUSE)) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vc_cec initialised", __FUNCTION__); + CLockObject lock(m_mutex); + m_bInitialised = true; + } + else + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec could not be initialised", __FUNCTION__); + } + + return true; +} + +uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void) +{ + uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS); + if (!IsInitialised()) + return iPA; + + if (vc_cec_get_physical_address(&iPA) == VCHIQ_SUCCESS) + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address = %04x", __FUNCTION__, iPA); + else + { + LIB_CEC->AddLog(CEC_LOG_WARNING, "%s - failed to get the physical address", __FUNCTION__); + iPA = CEC_INVALID_PHYSICAL_ADDRESS; + } + + return iPA; +} + +void CRPiCECAdapterCommunication::Close(void) +{ + { + CLockObject lock(m_mutex); + if (m_bInitialised) + m_bInitialised = false; + else + return; + } + UnregisterLogicalAddress(); + + // disable passive mode + vc_cec_set_passive(false); + + if (!g_bHostInited) + { + g_bHostInited = false; + bcm_host_deinit(); + } +} + +std::string CRPiCECAdapterCommunication::GetError(void) const +{ + std::string strError(m_strError); + return strError; +} + +cec_adapter_message_state CRPiCECAdapterCommunication::Write(const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool bIsReply) +{ + // ensure that the source LA is registered + if (!RegisterLogicalAddress(data.initiator)) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to register logical address %s (%X)", CCECTypeUtils::ToString(data.initiator), data.initiator); + return (data.initiator == data.destination) ? ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED : ADAPTER_MESSAGE_STATE_ERROR; + } + + if (!data.opcode_set && data.initiator == data.destination) + { + // registration of the logical address would have failed + return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + } + + return m_queue->Write(data, bIsReply) ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; +} + +uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void) +{ + return VC_CECSERVICE_VER; +} + +cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void) +{ + { + CLockObject lock(m_mutex); + if (m_logicalAddress != CECDEVICE_UNKNOWN) + return m_logicalAddress; + } + + CEC_AllDevices_T address; + return (vc_cec_get_logical_address(&address) == VCHIQ_SUCCESS) ? + (cec_logical_address)address : CECDEVICE_UNKNOWN; +} + +bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void) +{ + CLockObject lock(m_mutex); + if (m_logicalAddress == CECDEVICE_UNKNOWN || + m_logicalAddress == CECDEVICE_BROADCAST) + return true; + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - releasing previous logical address", __FUNCTION__); + m_bLogicalAddressChanged = false; + + vc_cec_release_logical_address(); + + return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged); +} + +bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address) +{ + { + CLockObject lock(m_mutex); + if (m_logicalAddress == address) + return true; + } + + if (!UnregisterLogicalAddress()) + return false; + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - registering address %x", __FUNCTION__, address); + + CLockObject lock(m_mutex); + m_bLogicalAddressChanged = false; + vc_cec_poll_address((CEC_AllDevices_T)address); + + // register the new LA + int iRetval = vc_cec_set_logical_address((CEC_AllDevices_T)address, (CEC_DEVICE_TYPE_T)CCECTypeUtils::GetType(address), CEC_VENDOR_ID_BROADCOM); + if (iRetval != VCHIQ_SUCCESS) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec_set_logical_address(%X) returned %s (%d)", __FUNCTION__, address, ToString((VC_CEC_ERROR_T)iRetval), iRetval); + return false; + } + + return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged); +} + +cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void) +{ + cec_logical_addresses addresses; addresses.Clear(); + cec_logical_address current = GetLogicalAddress(); + if (current != CECDEVICE_UNKNOWN) + addresses.Set(current); + + return addresses; +} + +bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) +{ + // the current generation RPi only supports 1 LA, so just ensure that the primary address is registered + return SupportsSourceLogicalAddress(addresses.primary) && + RegisterLogicalAddress(addresses.primary); +} + +void CRPiCECAdapterCommunication::InitHost(void) +{ + if (!g_bHostInited) + { + g_bHostInited = true; + bcm_host_init(); + } +} + +#endif diff --git a/src/lib/adapter/RPi/RPiCECAdapterCommunication.h b/src/lib/adapter/RPi/RPiCECAdapterCommunication.h new file mode 100644 index 0000000..530aec1 --- /dev/null +++ b/src/lib/adapter/RPi/RPiCECAdapterCommunication.h @@ -0,0 +1,107 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#if defined(HAVE_RPI_API) + +#include "lib/adapter/AdapterCommunication.h" +#include "lib/platform/threads/mutex.h" + +extern "C" { +#include +#include +} + +namespace CEC +{ + class CRPiCECAdapterMessageQueue; + + class CRPiCECAdapterCommunication : public IAdapterCommunication + { + public: + /*! + * @brief Create a new USB-CEC communication handler. + * @param callback The callback to use for incoming CEC commands. + */ + CRPiCECAdapterCommunication(IAdapterCommunicationCallback *callback); + virtual ~CRPiCECAdapterCommunication(void); + + /** @name IAdapterCommunication implementation */ + ///{ + bool Open(uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT, bool bSkipChecks = false, bool bStartListening = true); + void Close(void); + bool IsOpen(void) { return m_bInitialised; }; + std::string GetError(void) const; + cec_adapter_message_state Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply); + + bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; }; + bool StartBootloader(void) { return false; }; + bool SetLogicalAddresses(const cec_logical_addresses &addresses); + cec_logical_addresses GetLogicalAddresses(void); + bool PingAdapter(void) { return m_bInitialised; }; + uint16_t GetFirmwareVersion(void); + uint32_t GetFirmwareBuildDate(void) { return 0; }; + bool IsRunningLatestFirmware(void) { return true; }; + bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; }; + bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; }; + std::string GetPortName(void) { std::string strReturn("RPI"); return strReturn; }; + uint16_t GetPhysicalAddress(void); + bool SetControlledMode(bool UNUSED(controlled)) { return true; }; + cec_vendor_id GetVendorId(void) { return CEC_VENDOR_BROADCOM; } + bool SupportsSourceLogicalAddress(const cec_logical_address address) { return address > CECDEVICE_TV && address < CECDEVICE_BROADCAST; } + ///} + + bool IsInitialised(void); + void OnDataReceived(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4); + + static void InitHost(void); + + private: + cec_logical_address GetLogicalAddress(void); + bool UnregisterLogicalAddress(void); + bool RegisterLogicalAddress(const cec_logical_address address); + int InitHostCEC(void); + + bool m_bInitialised; /**< true when the connection is initialised, false otherwise */ + std::string m_strError; /**< current error message */ + CRPiCECAdapterMessageQueue *m_queue; + cec_logical_address m_logicalAddress; + + bool m_bLogicalAddressChanged; + PLATFORM::CCondition m_logicalAddressCondition; + PLATFORM::CMutex m_mutex; + VCHI_INSTANCE_T m_vchi_instance; + VCHI_CONNECTION_T * m_vchi_connection; + }; +}; + +#endif diff --git a/src/lib/adapter/RPi/RPiCECAdapterDetection.cpp b/src/lib/adapter/RPi/RPiCECAdapterDetection.cpp new file mode 100644 index 0000000..818d5ed --- /dev/null +++ b/src/lib/adapter/RPi/RPiCECAdapterDetection.cpp @@ -0,0 +1,59 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" + +#if defined(HAVE_RPI_API) +#include "RPiCECAdapterDetection.h" + +extern "C" { +#include +#include +} + +using namespace CEC; + +bool CRPiCECAdapterDetection::FindAdapter(void) +{ + uint8_t iResult; + + VCHI_INSTANCE_T vchiq_instance; + if ((iResult = vchi_initialise(&vchiq_instance)) != VCHIQ_SUCCESS) + return false; + + if ((iResult = vchi_connect(NULL, 0, vchiq_instance)) != VCHIQ_SUCCESS) + return false; + + return true; +} + +#endif diff --git a/src/lib/adapter/RPi/RPiCECAdapterDetection.h b/src/lib/adapter/RPi/RPiCECAdapterDetection.h new file mode 100644 index 0000000..34f2234 --- /dev/null +++ b/src/lib/adapter/RPi/RPiCECAdapterDetection.h @@ -0,0 +1,41 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +namespace CEC +{ + class CRPiCECAdapterDetection + { + public: + static bool FindAdapter(void); + }; +} diff --git a/src/lib/adapter/RPi/RPiCECAdapterMessageQueue.cpp b/src/lib/adapter/RPi/RPiCECAdapterMessageQueue.cpp new file mode 100644 index 0000000..df9a374 --- /dev/null +++ b/src/lib/adapter/RPi/RPiCECAdapterMessageQueue.cpp @@ -0,0 +1,209 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" + +#if defined(HAVE_RPI_API) +#include "RPiCECAdapterMessageQueue.h" + +// use vc_cec_send_message2() if defined and vc_cec_send_message() if not +//#define RPI_USE_SEND_MESSAGE2 + +#include "RPiCECAdapterCommunication.h" +#include "lib/LibCEC.h" +#include "lib/CECTypeUtils.h" +#include "lib/platform/util/StdString.h" + +extern "C" { +#include +#include +} + +using namespace std; +using namespace CEC; +using namespace PLATFORM; + +#define LIB_CEC m_com->m_callback->GetLib() + +CRPiCECAdapterMessageQueueEntry::CRPiCECAdapterMessageQueueEntry(CRPiCECAdapterMessageQueue *queue, const cec_command &command) : + m_queue(queue), + m_command(command), + m_retval(VC_CEC_ERROR_NO_ACK), + m_bSucceeded(false) +{ + +} + +void CRPiCECAdapterMessageQueueEntry::Broadcast(void) +{ + CLockObject lock(m_mutex); + m_condition.Broadcast(); +} + +bool CRPiCECAdapterMessageQueueEntry::MessageReceived(cec_opcode opcode, cec_logical_address initiator, cec_logical_address destination, uint32_t response) +{ + if ((!m_command.opcode_set || m_command.opcode == opcode) && + m_command.initiator == initiator && + m_command.destination == destination) + { + CLockObject lock(m_mutex); + m_retval = response; + m_bSucceeded = true; + m_condition.Signal(); + return true; + } + + return false; +} + +bool CRPiCECAdapterMessageQueueEntry::Wait(uint32_t iTimeout) +{ + bool bReturn(false); + /* wait until we receive a signal when the tranmission succeeded */ + { + CLockObject lock(m_mutex); + bReturn = m_bSucceeded ? true : m_condition.Wait(m_mutex, m_bSucceeded, iTimeout); + m_bWaiting = false; + + if (bReturn) + bReturn = m_retval == VCHIQ_SUCCESS; + } + return bReturn; +} + +bool CRPiCECAdapterMessageQueueEntry::IsWaiting(void) +{ + CLockObject lock(m_mutex); + return m_bWaiting; +} + +void CRPiCECAdapterMessageQueue::Clear(void) +{ + CLockObject lock(m_mutex); + m_messages.clear(); +} + +void CRPiCECAdapterMessageQueue::MessageReceived(cec_opcode opcode, cec_logical_address initiator, cec_logical_address destination, uint32_t response) +{ + bool bHandled(false); + CLockObject lock(m_mutex); + /* send the received message to each entry in the queue until it is handled */ + for (map::iterator it = m_messages.begin(); !bHandled && it != m_messages.end(); it++) + bHandled = it->second->MessageReceived(opcode, initiator, destination, response); + + if (!bHandled) + LIB_CEC->AddLog(CEC_LOG_WARNING, "unhandled response received"); +} + +bool CRPiCECAdapterMessageQueue::Write(const cec_command &command, bool bIsReply) +{ + CRPiCECAdapterMessageQueueEntry *entry = new CRPiCECAdapterMessageQueueEntry(this, command); + uint64_t iEntryId(0); + /* add to the wait for ack queue */ + { + CLockObject lock(m_mutex); + iEntryId = m_iNextMessage++; + m_messages.insert(make_pair(iEntryId, entry)); + } + +#if defined(RPI_USE_SEND_MESSAGE2) + VC_CEC_MESSAGE_T message; + message.initiator = (CEC_AllDevices_T)command.initiator; + message.follower = (CEC_AllDevices_T)command.destination; + message.length = 1; + + if (command.opcode_set) + { + message.length += 1; + message.payload[0] = command.opcode; + + message.length += command.parameters.size; + for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++) + message.payload[iPtr + 1] = command.parameters.At(iPtr); + } + + CStdString strDump; + strDump.Format("len = %d, payload = %X%X", message.length, (int)message.initiator, (int)message.follower); + for (uint8_t iPtr = 0; iPtr < message.length - 1; iPtr++) + strDump.AppendFormat(":%02X", message.payload[iPtr]); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending data: %s", strDump.c_str()); + + int iReturn = vc_cec_send_message2(&message); +#else + uint8_t payload[32]; + uint32_t iLength(0); + + if (command.opcode_set) + { + iLength += 1; + payload[0] = command.opcode; + + iLength += command.parameters.size; + for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++) + payload[iPtr + 1] = command.parameters.At(iPtr); + } + + CStdString strDump; + strDump.Format("len = %d, payload = %X%X", iLength, (int)command.initiator, (int)command.destination); + for (uint8_t iPtr = 0; iPtr < iLength; iPtr++) + strDump.AppendFormat(":%02X", payload[iPtr]); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending data: %s", strDump.c_str()); + + int iReturn = vc_cec_send_message((uint32_t)command.destination, (uint8_t*)&payload, iLength, bIsReply); +#endif + + if (iReturn != VCHIQ_SUCCESS) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending command '%s' failed (%d)", command.opcode_set ? CCECTypeUtils::ToString(command.opcode) : "POLL", iReturn); + delete (entry); + return false; + } + + bool bReturn(true); + if (entry) + { + if (!entry->Wait(CEC_DEFAULT_TRANSMIT_WAIT)) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' was not acked by the controller", command.opcode_set ? CCECTypeUtils::ToString(command.opcode) : "POLL"); + bReturn = false; + } + + CLockObject lock(m_mutex); + m_messages.erase(iEntryId); + delete entry; + } + + return bReturn; +} + +#endif + diff --git a/src/lib/adapter/RPi/RPiCECAdapterMessageQueue.h b/src/lib/adapter/RPi/RPiCECAdapterMessageQueue.h new file mode 100644 index 0000000..35d2859 --- /dev/null +++ b/src/lib/adapter/RPi/RPiCECAdapterMessageQueue.h @@ -0,0 +1,117 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "lib/platform/util/buffer.h" +#include + +namespace CEC +{ + class CRPiCECAdapterCommunication; + class CRPiCECAdapterMessageQueue; + + class CRPiCECAdapterMessageQueueEntry + { + public: + CRPiCECAdapterMessageQueueEntry(CRPiCECAdapterMessageQueue *queue, const cec_command &command); + virtual ~CRPiCECAdapterMessageQueueEntry(void) {} + + /*! + * @brief Signal waiting threads + */ + void Broadcast(void); + + bool MessageReceived(cec_opcode opcode, cec_logical_address initiator, cec_logical_address destination, uint32_t response); + + /*! + * @brief Wait for a response to this command. + * @param iTimeout The timeout to use while waiting. + * @return True when a response was received before the timeout passed, false otherwise. + */ + bool Wait(uint32_t iTimeout); + + /*! + * @return True while a thread is waiting for a signal or isn't waiting yet, false otherwise. + */ + bool IsWaiting(void); + + /*! + * @return The command that was sent in human readable form. + */ + const char *ToString(void) const { return "CEC transmission"; } + + CRPiCECAdapterMessageQueue * m_queue; + bool m_bWaiting; /**< true while a thread is waiting or when it hasn't started waiting yet */ + PLATFORM::CCondition m_condition; /**< the condition to wait on */ + PLATFORM::CMutex m_mutex; /**< mutex for changes to this class */ + cec_command m_command; + uint32_t m_retval; + bool m_bSucceeded; + }; + + class CRPiCECAdapterMessageQueue + { + friend class CRPiCECAdapterMessageQueueEntry; + + public: + /*! + * @brief Create a new message queue. + * @param com The communication handler callback to use. + * @param iQueueSize The outgoing message queue size. + */ + CRPiCECAdapterMessageQueue(CRPiCECAdapterCommunication *com) : + m_com(com), + m_iNextMessage(0) + { + } + + virtual ~CRPiCECAdapterMessageQueue(void) + { + Clear(); + } + + /*! + * @brief Signal and delete everything in the queue + */ + void Clear(void); + + void MessageReceived(cec_opcode opcode, cec_logical_address initiator, cec_logical_address destination, uint32_t response); + + bool Write(const cec_command &command, bool bIsReply); + + private: + CRPiCECAdapterCommunication * m_com; /**< the communication handler */ + PLATFORM::CMutex m_mutex; /**< mutex for changes to this class */ + std::map m_messages; /**< the outgoing message queue */ + uint64_t m_iNextMessage; /**< the index of the next message */ + }; +}; -- 2.34.1