added support for the Raspberry Pi. this needs a recent firmware for the Pi. libCEC...
authorLars Op den Kamp <lars@opdenkamp.eu>
Wed, 25 Jul 2012 11:04:12 +0000 (13:04 +0200)
committerLars Op den Kamp <lars@opdenkamp.eu>
Fri, 27 Jul 2012 15:05:08 +0000 (17:05 +0200)
17 files changed:
ChangeLog
README
configure.ac
debian/changelog
project/RPi/bootstrap.sh [new file with mode: 0755]
project/RPi/build-deps.sh [new file with mode: 0755]
project/RPi/build.sh [new file with mode: 0755]
project/RPi/config [new file with mode: 0644]
project/RPi/get-toolchain.sh [new file with mode: 0755]
src/lib/Makefile.am
src/lib/adapter/AdapterFactory.cpp
src/lib/adapter/RPi/RPiCECAdapterCommunication.cpp [new file with mode: 0644]
src/lib/adapter/RPi/RPiCECAdapterCommunication.h [new file with mode: 0644]
src/lib/adapter/RPi/RPiCECAdapterDetection.cpp [new file with mode: 0644]
src/lib/adapter/RPi/RPiCECAdapterDetection.h [new file with mode: 0644]
src/lib/adapter/RPi/RPiCECAdapterMessageQueue.cpp [new file with mode: 0644]
src/lib/adapter/RPi/RPiCECAdapterMessageQueue.h [new file with mode: 0644]

index fb77809889a6719248e169c17c6b626e528f4c0c..12e279253ebcdaf43b21243cdfe27cbcfc17dd75 100644 (file)
--- 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 c8b2aef9ab805eaabc1771fe4f5843ead11af515..5c7364b19882ad5b765448ab7097e4ef45ad2179 100644 (file)
--- 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 ===
 ===============================================================================
index 1ae91b0d86295d7ac96876a560aeff6408e64bfb..ae1b5a545515ace259e374a75b0bc079c7dffbb4 100644 (file)
@@ -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 <interface/vmcs_host/vc_cecservice.h>
+#include <interface/vchiq_arm/vchiq_if.h>
+#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))
index fb77809889a6719248e169c17c6b626e528f4c0c..12e279253ebcdaf43b21243cdfe27cbcfc17dd75 100644 (file)
@@ -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 (executable)
index 0000000..83f184c
--- /dev/null
@@ -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 (executable)
index 0000000..be6b22a
--- /dev/null
@@ -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 (executable)
index 0000000..1d684e1
--- /dev/null
@@ -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 (file)
index 0000000..b81021d
--- /dev/null
@@ -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 (executable)
index 0000000..96e3634
--- /dev/null
@@ -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
+
index d4340ecdbe40908213cb3f74659760570c180f3c..7ea1e99dd5bf742a66149f82c2d624369b535c4a 100644 (file)
@@ -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@
index ec216abf9df0235a192bc55c223714d4f1315bdf..9ee9cc7c664516b51782ef06fcd8b38952fb2763 100644 (file)
 #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 (file)
index 0000000..5e1efd4
--- /dev/null
@@ -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       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "env.h"
+
+#if defined(HAVE_RPI_API)
+#include "RPiCECAdapterCommunication.h"
+
+extern "C" {
+#include <bcm_host.h>
+}
+
+#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<CRPiCECAdapterCommunication *>(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 (file)
index 0000000..530aec1
--- /dev/null
@@ -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       <license@pulse-eight.com>
+ *     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 <interface/vmcs_host/vc_cecservice.h>
+#include <interface/vchiq_arm/vchiq_if.h>
+}
+
+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<bool>  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 (file)
index 0000000..818d5ed
--- /dev/null
@@ -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       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "env.h"
+
+#if defined(HAVE_RPI_API)
+#include "RPiCECAdapterDetection.h"
+
+extern "C" {
+#include <interface/vmcs_host/vc_cecservice.h>
+#include <interface/vchiq_arm/vchiq_if.h>
+}
+
+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 (file)
index 0000000..34f2234
--- /dev/null
@@ -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       <license@pulse-eight.com>
+ *     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 (file)
index 0000000..df9a374
--- /dev/null
@@ -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       <license@pulse-eight.com>
+ *     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 <interface/vmcs_host/vc_cecservice.h>
+#include <interface/vchiq_arm/vchiq.h>
+}
+
+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<uint64_t, CRPiCECAdapterMessageQueueEntry *>::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 (file)
index 0000000..35d2859
--- /dev/null
@@ -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       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "lib/platform/util/buffer.h"
+#include <map>
+
+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<bool>   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<uint64_t, CRPiCECAdapterMessageQueueEntry *>     m_messages;               /**< the outgoing message queue */
+    uint64_t                                                  m_iNextMessage;           /**< the index of the next message */
+  };
+};