]> git.etc.gen.nz Git - sapphire-remote.git/commitdiff
First, belated, commit (started work in 2016)
authorAndrew Ruthven <andrew@etc.gen.nz>
Sat, 16 Feb 2019 07:11:32 +0000 (07:11 +0000)
committerAndrew Ruthven <andrew@etc.gen.nz>
Sat, 16 Feb 2019 07:17:08 +0000 (07:17 +0000)
26 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
common.postinst [new file with mode: 0755]
debian/.gitignore [new file with mode: 0644]
debian/README.Debian [new file with mode: 0755]
debian/changelog [new file with mode: 0755]
debian/compat [new file with mode: 0755]
debian/control [new file with mode: 0755]
debian/copyright [new file with mode: 0755]
debian/dirs.sapphire-remote-dkms [new file with mode: 0755]
debian/files [new file with mode: 0644]
debian/postinst.sapphire-remote-dkms [new file with mode: 0755]
debian/prerm.sapphire-remote-dkms [new file with mode: 0755]
debian/rules [new file with mode: 0755]
debian/service.sapphire-remote [new file with mode: 0644]
sapphire-remote-6.6.dkms.tar.gz [new file with mode: 0644]
sapphire-remote-6.6/Makefile [new file with mode: 0644]
sapphire-remote-6.6/README.txt [new file with mode: 0644]
sapphire-remote-6.6/dkms.conf [new file with mode: 0644]
sapphire-remote-6.6/extract_keydefs.sh [new file with mode: 0755]
sapphire-remote-6.6/keymap.default [new file with mode: 0644]
sapphire-remote-6.6/sapphire.c [new file with mode: 0644]
sapphire-remote-6.6/sapphire.h [new file with mode: 0644]
sapphire-remote-6.6/sapphire_keymap.sh.part1 [new file with mode: 0644]
sapphire-remote-6.6/sapphire_keymap.sh.part3 [new file with mode: 0644]
sapphire-remote-6.6/sapphire_startup.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..99ee691
--- /dev/null
@@ -0,0 +1,2 @@
+build-stamp
+configure-stamp
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..57e13d8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,28 @@
+#/usr/bin/make
+SRC = $(DESTDIR)/usr/src
+SHARE = $(DESTDIR)/usr/share/$(NAME)-dkms
+
+all:
+
+clean:
+
+install:
+
+#source tree
+ifeq ("$(wildcard $(NAME)-$(VERSION))", "$(NAME)-$(VERSION)")
+       install -d "$(SRC)"
+       cp -a $(NAME)-$(VERSION) $(SRC)
+       chmod 644 -R "$(SRC)/$(NAME)-$(VERSION)"
+endif
+
+#tarball, possibly with binaries
+ifeq ("$(wildcard $(NAME)-$(VERSION).dkms.tar.gz)", "$(NAME)-$(VERSION).dkms.tar.gz")
+       install -d "$(SHARE)"
+       install -m 644 $(NAME)-$(VERSION).dkms.tar.gz "$(SHARE)"
+endif
+
+#postinst, only if we are supporting legacy mode
+ifeq ("$(wildcard common.postinst)", "common.postinst")
+       install -d "$(SHARE)"
+       install -m 755 $(PREFIX)/usr/lib/dkms/common.postinst $(SHARE)/postinst
+endif
diff --git a/common.postinst b/common.postinst
new file mode 100755 (executable)
index 0000000..0c8cfc1
--- /dev/null
@@ -0,0 +1,295 @@
+#!/bin/sh
+# Copyright (C) 2002-2005 Flavio Stanchina
+# Copyright (C) 2005-2006 Aric Cyr
+# Copyright (C) 2007 Mario Limonciello
+# Copyright (C) 2009 Alberto Milone
+
+set -e
+
+uname_s=$(uname -s)
+
+_get_kernel_dir() {
+    KVER=$1
+    case ${uname_s} in
+       Linux)          DIR="/lib/modules/$KVER/build" ;;
+       GNU/kFreeBSD)   DIR="/usr/src/kfreebsd-headers-$KVER/sys" ;;
+    esac
+    echo $DIR
+}
+
+_check_kernel_dir() {
+    DIR=$(_get_kernel_dir $1)
+    case ${uname_s} in
+       Linux)          test -e $DIR/include ;;
+       GNU/kFreeBSD)   test -e $DIR/kern && test -e $DIR/conf/kmod.mk ;;
+       *)              return 1 ;;
+    esac
+    return $?
+}
+
+# Check the existence of a kernel named as $1
+_is_kernel_name_correct() {
+    CORRECT="no"
+    KERNEL_NAME=$1
+
+    for kernel in /boot/config-*; do
+        KERNEL=${kernel#*-}
+        if [ "${KERNEL}" = "${KERNEL_NAME}" ]; then
+            CORRECT="yes"
+            break
+        fi
+    done
+
+    echo $CORRECT
+}
+
+
+# Get the most recent kernel on Debian based systems. This keeps
+# into account both the version and the ABI. If the current kernel
+# is the most recent kernel then the function will print a null string.
+_get_newest_kernel_debian() {
+    NEWEST_KERNEL=
+    NEWEST_VERSION=
+    NEWEST_ABI=
+
+    for kernel in /boot/config-*; do
+        KERNEL=${kernel#*-}
+        KERNEL_VERSION=${KERNEL%%-*}
+        ABI=${KERNEL#*-}
+        ABI=${ABI%%-*}
+
+        if [ -z "$NEWEST_KERNEL" ]; then
+            # The 1st time get a version which is bigger than $1
+            COMPARE_TO=$1
+        else
+            # Get the biggest version
+            COMPARE_TO="$NEWEST_VERSION-$NEWEST_ABI"
+        fi
+
+        # if $kernel is greater than $COMPARE_TO
+        if [ `dpkg --compare-versions "$KERNEL_VERSION-$ABI" gt "$COMPARE_TO" && echo "yes" || \
+              echo "no"` = "yes" ]; then
+            NEWEST_KERNEL=$KERNEL
+            NEWEST_VERSION=$KERNEL_VERSION
+            NEWEST_ABI=$ABI
+        fi
+    done
+
+    echo "$NEWEST_KERNEL"
+}
+
+# Get the most recent kernel in Rhel based systems. If the current kernel
+# is the most recent kernel then the function will print a null string.
+_get_newest_kernel_rhel() {
+    NEWEST_KERNEL=
+
+    LAST_INSTALLED_KERNEL=$(rpm -q --whatprovides kernel  --last | grep kernel -m1 | cut -f1 -d' ')
+
+    LIK_FORMATTED_NAME=$(rpm -q $LAST_INSTALLED_KERNEL --queryformat="%{VERSION}-%{RELEASE}.%{ARCH}\n")
+
+    if [ `echo $LIK_FORMATTED_NAME | grep 2.6 >/dev/null` ]; then
+        # Fedora and Suse
+        NEWEST_KERNEL=$LIK_FORMATTED_NAME
+    else
+        # Hack for Mandriva where $LIK_FORMATTED_NAME is broken
+        LIK_NAME=$(rpm -q $LAST_INSTALLED_KERNEL --queryformat="%{NAME}\n")
+        LIK_TYPE=${LIK_NAME#kernel-}
+        LIK_TYPE=${LIK_TYPE%%-*}
+        LIK_STRIPPED=${LIK_NAME#kernel-}
+        LIK_STRIPPED=${LIK_STRIPPED#$LIK_TYPE-}
+        LIK_STRIPPED_BASE=${LIK_STRIPPED%%-*}
+        LIK_STRIPPED_END=${LIK_STRIPPED#$LIK_STRIPPED_BASE-}
+        LIK_FINAL=$LIK_STRIPPED_BASE-$LIK_TYPE-$LIK_STRIPPED_END
+
+        NEWEST_KERNEL=$LIK_FINAL
+    fi
+
+    echo $NEWEST_KERNEL
+}
+
+# Get the newest kernel on Debian and Rhel based systems.
+get_newest_kernel() {
+    NEWEST_KERNEL=
+    # Try Debian first as rpm can be installed in Debian based distros
+    if [ -e /usr/bin/dpkg ]; then
+        # If DEB based
+        CURRENT_KERNEL=$1
+        CURRENT_VERSION=${CURRENT_KERNEL%%-*}
+        CURRENT_ABI=${CURRENT_KERNEL#*-}
+        CURRENT_FLAVOUR=${CURRENT_ABI#*-}
+        CURRENT_ABI=${CURRENT_ABI%%-*}
+        NEWEST_KERNEL=$(_get_newest_kernel_debian "$CURRENT_VERSION-$CURRENT_ABI")
+
+    elif [ `which rpm >/dev/null` ]; then
+        # If RPM based
+        NEWEST_KERNEL=$(_get_newest_kernel_rhel)
+    fi
+
+    # Make sure that kernel name that we extracted corresponds to an installed
+    # kernel
+    if [ -n "$NEWEST_KERNEL" ] && [ `_is_kernel_name_correct $NEWEST_KERNEL` = "no" ]; then
+        NEWEST_KERNEL=
+    fi
+
+    echo $NEWEST_KERNEL
+}
+
+NAME=$1
+VERSION=$2
+TARBALL_ROOT=$3
+ARCH=$4
+UPGRADE=$5
+
+if [ -z "$NAME" ] || [ -z "$VERSION" ]; then
+    echo "Need NAME, and VERSION defined"
+    echo "ARCH is optional"
+    exit 1
+fi
+
+KERNELS=$(ls /lib/modules/ 2>/dev/null || true)
+CURRENT_KERNEL=$(uname -r)
+
+#We never want to keep an older version side by side to prevent conflicts
+if [ -e "/var/lib/dkms/$NAME/$VERSION" ]; then
+    echo "Removing old $NAME-$VERSION DKMS files..."
+    dkms remove -m $NAME -v $VERSION --all
+fi
+
+#Load new files, by source package and by tarball
+if [ -f "$TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz" ]; then
+    if ! dkms ldtarball --archive "$TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz"; then
+        echo ""
+        echo ""
+        echo "Unable to load DKMS tarball $TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz."
+        echo "Common causes include: "
+        echo " - You must be using DKMS 2.1.0.0 or later to support binaries only"
+        echo "   distribution specific archives."
+        echo " - Corrupt distribution specific archive"
+        echo ""
+        echo ""
+        exit 2
+    fi
+elif [ -d "/usr/src/$NAME-$VERSION" ]; then
+    echo "Loading new $NAME-$VERSION DKMS files..."
+    dkms add -m $NAME -v $VERSION > /dev/null
+fi
+
+# On 1st installation, let us look for a directory
+# in /lib/modules which matches `uname -r`. If none
+# is found it is possible that buildd is being used
+# and that uname -r is giving us the name of the
+# kernel used by the buildd machine.
+#
+# If this is the case we try to build the kernel
+# module for each kernel which has a directory in
+# /lib/modules. Furthermore we will have to tell
+# DKMS which architecture it should build the module
+# for (e.g. if the buildd machine is using a
+# 2.6.24-23-xen 64bit kernel).
+#
+# NOTE: if the headers are not installed then the
+#       module won't be built, as usual
+if [ -z "$UPGRADE" ]; then
+    echo "First Installation: checking all kernels..."
+    for KERNEL in $KERNELS; do
+        if [ ${KERNEL} = ${CURRENT_KERNEL} ]; then
+            # Kernel found
+            KERNELS=$CURRENT_KERNEL
+            break
+        fi
+    done
+else
+    KERNELS=$CURRENT_KERNEL
+fi
+
+# Here we look for the most recent kernel so that we can
+# build the module for it (in addition to doing it for the
+# current kernel.
+NEWEST_KERNEL=$(get_newest_kernel "$KERNELS")
+
+# If the current kernel doesn't come from the host of a chroot
+if [ `_is_kernel_name_correct $CURRENT_KERNEL` = "yes" ]; then
+    # See if it's worth building the module for both the newest kernel
+    # and for the current kernel
+    if [ -n "$NEWEST_KERNEL" ] && [ ${CURRENT_KERNEL} != ${NEWEST_KERNEL} ]; then
+        echo "Building for $CURRENT_KERNEL and $NEWEST_KERNEL"
+        KERNELS="$CURRENT_KERNEL $NEWEST_KERNEL"
+    else
+        echo "Building only for $CURRENT_KERNEL"
+    fi
+# The current kernel is not useful as it's the host's
+else
+    echo "It is likely that $CURRENT_KERNEL belongs to a chroot's host"
+
+    # Let's use only the newest kernel
+    if [ -n "$NEWEST_KERNEL" ]; then
+        KERNELS="$NEWEST_KERNEL"
+        echo "Building only for $NEWEST_KERNEL"
+    fi
+fi
+
+if [ -n "$ARCH" ]; then
+    if which lsb_release >/dev/null && [ $(lsb_release -s -i) = "Ubuntu" ]; then
+        case $ARCH in
+            amd64)
+                ARCH="x86_64"
+                ;;
+            lpia|i?86)
+                ARCH="i686"
+                ;;
+        esac
+    fi
+    echo "Building for architecture $ARCH"
+    ARCH="-a $ARCH"
+fi
+
+for KERNEL in $KERNELS; do
+    dkms_status=`dkms status -m $NAME -v $VERSION -k $KERNEL $ARCH`
+    if [ `echo $KERNEL | grep -c "BOOT"` -gt 0 ]; then
+        echo ""
+        echo "Module build and install for $KERNEL was skipped as "
+        echo "it is a BOOT variant"
+        continue
+    fi
+
+
+    #if the module isn't yet built, try to build it
+    if [ `echo $dkms_status | grep -c ": built"` -eq 0 ]; then
+        if [ ! -L /var/lib/dkms/$NAME/$VERSION/source ]; then
+            echo "This package appears to be a binaries-only package"
+            echo " you will not be able to build against kernel $KERNEL"
+            echo " since the package source was not provided"
+            continue
+        fi
+        if _check_kernel_dir $KERNEL; then
+            echo "Building initial module for $KERNEL"
+            set +e
+            dkms build -m $NAME -v $VERSION -k $KERNEL $ARCH > /dev/null
+            case $? in
+            9)
+                set -e
+                echo "Skipped."
+                continue
+                ;;
+            0)
+                set -e
+                echo "Done."
+                ;;
+            *)
+                exit $?
+                ;;
+            esac
+            dkms_status=`dkms status -m $NAME -v $VERSION -k $KERNEL $ARCH`
+        else
+            echo "Module build for the currently running kernel was skipped since the"
+            echo "kernel source for this kernel does not seem to be installed."
+        fi
+    fi
+
+    #if the module is built (either pre-built or just now), install it
+    if [ `echo $dkms_status | grep -c ": built"` -eq 1 ] && 
+       [ `echo $dkms_status | grep -c ": installed"` -eq 0 ]; then
+        dkms install -m $NAME -v $VERSION -k $KERNEL $ARCH
+    fi
+done
+
diff --git a/debian/.gitignore b/debian/.gitignore
new file mode 100644 (file)
index 0000000..033aa4a
--- /dev/null
@@ -0,0 +1,4 @@
+*.log
+*.substvars
+sapphire-remote-dkms/
+sapphire-remote/
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100755 (executable)
index 0000000..befa406
--- /dev/null
@@ -0,0 +1,5 @@
+sapphire-remote DKMS module for Debian
+
+This package was automatically generated by the DKMS system,
+for distribution on Debian based operating systems.
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100755 (executable)
index 0000000..5c120b4
--- /dev/null
@@ -0,0 +1,6 @@
+sapphire-remote-dkms (6.6) stable; urgency=low
+
+  * Automatically packaged by DKMS.
+
+ -- Dynamic Kernel Modules Support Team <pkg-dkms-maint@lists.alioth.debian.org>  Sun, 21 Aug 2016 21:31:06 +0000
+
diff --git a/debian/compat b/debian/compat
new file mode 100755 (executable)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100755 (executable)
index 0000000..ac8ac30
--- /dev/null
@@ -0,0 +1,16 @@
+Source: sapphire-remote-dkms
+Section: misc
+Priority: optional
+Maintainer: Dynamic Kernel Modules Support Team <pkg-dkms-maint@lists.alioth.debian.org>
+Build-Depends: debhelper (>= 7), dkms, gawk
+Standards-Version: 3.8.1
+
+Package: sapphire-remote-dkms
+Architecture: all
+Depends: dkms (>= 1.95), ${misc:Depends}
+Description: sapphire-remote driver in DKMS format.
+
+Package: sapphire-remote
+Architecture: all
+Depends: sapphire-remote-dkms, ${misc:Depends}
+Description: sapphire-remote tools
diff --git a/debian/copyright b/debian/copyright
new file mode 100755 (executable)
index 0000000..ad983f3
--- /dev/null
@@ -0,0 +1,2 @@
+
+This copyright has not been completed by the author of this package.
diff --git a/debian/dirs.sapphire-remote-dkms b/debian/dirs.sapphire-remote-dkms
new file mode 100755 (executable)
index 0000000..b601f22
--- /dev/null
@@ -0,0 +1 @@
+usr/src
diff --git a/debian/files b/debian/files
new file mode 100644 (file)
index 0000000..e2bfa16
--- /dev/null
@@ -0,0 +1,2 @@
+sapphire-remote-dkms_6.6_all.deb misc optional
+sapphire-remote_6.6_all.deb misc optional
diff --git a/debian/postinst.sapphire-remote-dkms b/debian/postinst.sapphire-remote-dkms
new file mode 100755 (executable)
index 0000000..5714e0b
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Copyright (C) 2002-2005 Flavio Stanchina
+# Copyright (C) 2005-2006 Aric Cyr
+# Copyright (C) 2007 Mario Limonciello
+# Copyright (C) 2009 Alberto Milone
+
+set -e
+
+NAME=sapphire-remote
+PACKAGE_NAME=$NAME-dkms
+DEB_NAME=$(echo $PACKAGE_NAME | sed 's,_,-,')
+CVERSION=`dpkg-query -W -f='${Version}' $DEB_NAME | awk -F "-" '{print $1}' | cut -d\: -f2`
+ARCH=`dpkg --print-architecture`
+
+dkms_configure () {
+       for POSTINST in /usr/lib/dkms/common.postinst "/usr/share/$PACKAGE_NAME/postinst"; do
+               if [ -f "$POSTINST" ]; then
+                       "$POSTINST" "$NAME" "$CVERSION" "/usr/share/$PACKAGE_NAME" "$ARCH" "$2"
+                       return $?
+               fi
+               echo "WARNING: $POSTINST does not exist." >&2
+       done
+       echo "ERROR: DKMS version is too old and $PACKAGE_NAME was not" >&2
+       echo "built with legacy DKMS support." >&2
+       echo "You must either rebuild $PACKAGE_NAME with legacy postinst" >&2
+       echo "support or upgrade DKMS to a more current version." >&2
+       return 1
+}
+
+case "$1" in
+       configure)
+               dkms_configure
+       ;;
+
+       abort-upgrade|abort-remove|abort-deconfigure)
+       ;;
+
+       *)
+               echo "postinst called with unknown argument \`$1'" >&2
+               exit 1
+       ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/prerm.sapphire-remote-dkms b/debian/prerm.sapphire-remote-dkms
new file mode 100755 (executable)
index 0000000..cf927b9
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+NAME=sapphire-remote
+VERSION=6.6
+
+set -e
+
+case "$1" in
+    remove|upgrade|deconfigure)
+      if [  "`dkms status -m $NAME`" ]; then
+         dkms remove -m $NAME -v $VERSION --all
+      fi
+    ;;
+
+    failed-upgrade)
+    ;;
+
+    *)
+        echo "prerm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..0a2855e
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+DEB_NAME=sapphire-remote
+NAME=sapphire-remote
+VERSION=6.6
+
+configure: configure-stamp
+configure-stamp:
+       dh_testdir
+       touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp 
+       dh_testdir
+       $(MAKE)
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+       -$(MAKE) clean
+       dh_clean
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_prep
+       dh_installdirs
+       $(MAKE) DESTDIR=$(CURDIR)/debian/$(DEB_NAME)-dkms NAME=$(NAME) VERSION=$(VERSION) install
+
+binary-arch: build install
+
+binary-indep: build install
+       dh_testdir
+       dh_testroot
+       dh_link
+       dh_strip
+       dh_compress
+       dh_fixperms
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/debian/service.sapphire-remote b/debian/service.sapphire-remote
new file mode 100644 (file)
index 0000000..b528d7b
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Sapphire HID driver
+
+[Service]
+ExecStartPre=/usr/lib/sapphire-remote/sapphire_keymap.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/sapphire-remote-6.6.dkms.tar.gz b/sapphire-remote-6.6.dkms.tar.gz
new file mode 100644 (file)
index 0000000..dc482ac
Binary files /dev/null and b/sapphire-remote-6.6.dkms.tar.gz differ
diff --git a/sapphire-remote-6.6/Makefile b/sapphire-remote-6.6/Makefile
new file mode 100644 (file)
index 0000000..8fd1233
--- /dev/null
@@ -0,0 +1,86 @@
+#
+# Makefile and (un-)Installer for Sapphire remote control driver,
+#    by Mark Lord 2012-2015.
+#
+MODNAME=sapphire
+MODSOURCE=$(MODNAME).c
+CONFLICTS=hid_topseed
+BLACKLIST=/etc/modprobe.d/blacklist-$(CONFLICTS).conf
+KEYMAP_SCRIPT=sapphire_keymap.sh
+EXTRACT_KEYDEFS=extract_keydefs.sh
+INPUT_H=$(shell [ -e /usr/include/linux/input-event-codes.h ] && echo /usr/include/linux/input-event-codes.h || echo /usr/include/linux/input.h)
+
+## MODPARMS is not used any more, but we have to nuke old copies to prevent issues:
+MODPARMS=/etc/modprobe.d/$(MODNAME).conf
+
+CWD    = $(shell pwd -P)
+KVER  ?= $(shell uname -r)
+KDIR  = /lib/modules/$(KVER)
+obj-m += $(MODNAME).o
+
+#EXTRA_CFLAGS += -Werror
+
+default: $(INPUT_H) kmod $(KEYMAP_SCRIPT)
+
+$(INPUT_H):
+       @echo "ERROR: $@: not found."
+       @echo
+       @echo "Please install the system development libraries first."
+       @echo "Eg. on Ubuntu/Mint systems:  sudo apt-get install build-essential"
+       @echo
+       @exit 1
+
+kmod:
+       $(MAKE) -C $(KDIR)/build M=$(CWD) modules
+
+$(MODNAME).ko: $(MODSOURCE) $(MODNAME).h
+       $(MAKE) kmod
+
+$(KEYMAP_SCRIPT): $(INPUT_H) $(EXTRACT_KEYDEFS) $(KEYMAP_SCRIPT).part1 $(KEYMAP_SCRIPT).part3 $(MODNAME).h $(INPUT_H)
+       @bash -c "type -all gawk" >/dev/null 2>&1 || (\
+               echo ;\
+               echo "gawk not found, needed by installer; Aborted." ;\
+               echo "For Ubuntu/Debian based systems, try this:  sudo apt-get install gawk" ;\
+               echo ;\
+               exit 2 ;\
+       )
+       chmod 0755 $(EXTRACT_KEYDEFS)
+       ./$(EXTRACT_KEYDEFS) $(INPUT_H) $(MODNAME).h | cat $(KEYMAP_SCRIPT).part1 - $(KEYMAP_SCRIPT).part3 > $(KEYMAP_SCRIPT)
+       chmod 0755 $(KEYMAP_SCRIPT)
+
+$(BLACKLIST):
+       @echo "Creating $(BLACKLIST)"
+       @echo "## $(CONFLICTS) Conflicts with $(MODNAME) driver" >  $(BLACKLIST)
+       @echo "blacklist $(CONFLICTS)"                           >> $(BLACKLIST)
+
+modinstall: $(MODNAME).ko $(KEYMAP_SCRIPT)
+       $(MAKE) -C $(KDIR)/build M=$(CWD) modules_install || exit 0
+       @[ -e $(KDIR)/extra/sapphire.ko ] || exit 1
+       depmod $(KVER)
+       @if [ "$(KVER)" = "$$(uname -r)" ]; then \
+               rmmod $(MODNAME)   2>/dev/null ;\
+               rmmod $(CONFLICTS) 2>/dev/null ;\
+               ./sapphire_startup.sh ;\
+       else \
+               exit 0 ;\
+       fi
+
+clean:
+       $(MAKE) -C $(KDIR)/build M=$(CWD) clean
+       -rm -f $(KEYMAP_SCRIPT)
+
+clean_modparms:
+       @if [ -e $(MODPARAMS) ]; then rm -f $(MODPARMS) ; fi ; exit 0
+
+uninstall: clean clean_modparms
+       -rmmod $(MODNAME)
+       -rm -f $(BLACKLIST)
+       -rm -f /usr/local/bin/$(KEYMAP_SCRIPT)
+       -rm -f $(KDIR)/extra/$(MODNAME).ko
+       depmod
+
+install: $(MODNAME).ko clean_modparms modinstall $(BLACKLIST) $(KEYMAP_SCRIPT)
+       install -m 0755 $(KEYMAP_SCRIPT) /usr/local/bin/
+       install -m 0755 sapphire_startup.sh /usr/local/bin/
+
+all: install
diff --git a/sapphire-remote-6.6/README.txt b/sapphire-remote-6.6/README.txt
new file mode 100644 (file)
index 0000000..5a9a168
--- /dev/null
@@ -0,0 +1,450 @@
+Sapphire hid driver, by Mark Lord <mlord@pobox.com>, September/2015.
+http://rtr.ca/sapphire_remote/
+
+This package contains a standalone Linux "hid" driver
+for the Sapphire Theatrix I/R remote control.
+
+To build/install the driver, simply do this:
+
+    sudo make install
+
+That command will need to be repeated after any
+subsequent kernel upgrades.
+
+This driver competes with hid-topseed for control of the device.
+As of 2012, several distros are now building the hid-topseed driver
+into the main kernel image, preventing it from being blacklisted
+and unloaded at run time.  To work around this, there is now a script
+included in this package for unbinding the device(s) from hid-topseed.
+This script should be run from /etc/rc.local, by manually editing
+that file and adding this line:
+
+   /usr/local/bin/sapphire_startup.sh
+
+This driver comes pre-configured for use with Mythtv,
+with the default mappings for most buttons set to
+what Mythtv usually expects.
+
+You can customize the mappings, by copying/editing
+the keymap.default file, and then feeding your modified
+copy as a parameter to /usr/local/bin/sapphire_keymap.sh
+after each system startup.
+
+Or you can directly edit the default keymap that is
+built into the driver source code, but that runs a risk
+of conflicts when the driver gets updated in the future.
+
+Best method:  copy/edit the keymap.default file,
+and save it as:  /etc/sapphire.keymap
+
+Then ensure you have this line (from above) in /etc/rc.local:
+
+    /usr/local/bin/sapphire_startup.sh  ## already discussed above
+
+Done.
+
+One can also use /proc/sapphire to inject KEY_ presses into the system
+from a script, just as if they had been typed on a keyboard or remote.
+
+    Eg.  echo "SENDKEY 5" > /proc/sapphire    ## sends KEY_4 (defined as 5 in input.h).
+
+This feature could be used to control mythfrontend from external scripts,
+or to automate keyboard input for any other application as well.
+
+A special keyword "REPEAT_RATE" is recognized by /proc/sapphire,
+and enables modifying the ramping repeat attack rate and maximum repeat rate.
+Two values must be provided:  the number of times/second that the rate is
+adjusted, and the maxiumum number of repeats per second.
+The default values can be restored like this:
+
+    Eg.  echo "REPEAT_RATE 40 10" > /proc/sapphire
+
+The same syntax can also be used in a keymap file.
+
+* * * * * * * * * *
+
+Below is a handy list of most of the available KEY_* names
+for use with definitions in the keymap file.
+These were extracted from /usr/include/linux/input.h:
+
+KEY_0
+KEY_102ND
+KEY_1
+KEY_2
+KEY_3
+KEY_4
+KEY_5
+KEY_6
+KEY_7
+KEY_8
+KEY_9
+KEY_A
+KEY_AB
+KEY_ADDRESSBOOK
+KEY_AGAIN
+KEY_ALTERASE
+KEY_ANGLE
+KEY_APOSTROPHE
+KEY_ARCHIVE
+KEY_AUDIO
+KEY_AUX
+KEY_B
+KEY_BACK
+KEY_BACKSLASH
+KEY_BACKSPACE
+KEY_BASSBOOST
+KEY_BATTERY
+KEY_BLUE
+KEY_BLUETOOTH
+KEY_BOOKMARKS
+KEY_BREAK
+KEY_BRIGHTNESS_CYCLE
+KEY_BRIGHTNESSDOWN
+KEY_BRIGHTNESSUP
+KEY_BRIGHTNESS_ZERO
+KEY_BRL_DOT10
+KEY_BRL_DOT1
+KEY_BRL_DOT2
+KEY_BRL_DOT3
+KEY_BRL_DOT4
+KEY_BRL_DOT5
+KEY_BRL_DOT6
+KEY_BRL_DOT7
+KEY_BRL_DOT8
+KEY_BRL_DOT9
+KEY_C
+KEY_CALC
+KEY_CALENDAR
+KEY_CAMERA
+KEY_CAMERA_FOCUS
+KEY_CANCEL
+KEY_CAPSLOCK
+KEY_CD
+KEY_CHANNEL
+KEY_CHANNELDOWN
+KEY_CHANNELUP
+KEY_CHAT
+KEY_CLEAR
+KEY_CLOSE
+KEY_CLOSECD
+KEY_COFFEE
+KEY_COMMA
+KEY_COMPOSE
+KEY_COMPUTER
+KEY_CONFIG
+KEY_CONNECT
+KEY_CONTEXT_MENU
+KEY_COPY
+KEY_CUT
+KEY_CYCLEWINDOWS
+KEY_D
+KEY_DASHBOARD
+KEY_DATABASE
+KEY_DEL_EOL
+KEY_DEL_EOS
+KEY_DELETE
+KEY_DELETEFILE
+KEY_DEL_LINE
+KEY_DIGITS
+KEY_DIRECTION
+KEY_DIRECTORY
+KEY_DISPLAY_OFF
+KEY_DISPLAYTOGGLE
+KEY_DOCUMENTS
+KEY_DOLLAR
+KEY_DOT
+KEY_DOWN
+KEY_DVD
+KEY_E
+KEY_EDIT
+KEY_EDITOR
+KEY_EJECTCD
+KEY_EJECTCLOSECD
+KEY_EMAIL
+KEY_END
+KEY_ENTER
+KEY_EPG
+KEY_EQUAL
+KEY_ESC
+KEY_EURO
+KEY_EXIT
+KEY_F10
+KEY_F11
+KEY_F12
+KEY_F13
+KEY_F14
+KEY_F15
+KEY_F1
+KEY_F16
+KEY_F17
+KEY_F18
+KEY_F19
+KEY_F20
+KEY_F21
+KEY_F22
+KEY_F23
+KEY_F24
+KEY_F2
+KEY_F
+KEY_F3
+KEY_F4
+KEY_F5
+KEY_F6
+KEY_F7
+KEY_F8
+KEY_F9
+KEY_FASTFORWARD
+KEY_FAVORITES
+KEY_FILE
+KEY_FINANCE
+KEY_FIND
+KEY_FIRST
+KEY_FN
+KEY_FN_1
+KEY_FN_2
+KEY_FN_B
+KEY_FN_D
+KEY_FN_E
+KEY_FN_ESC
+KEY_FN_F
+KEY_FN_F10
+KEY_FN_F1
+KEY_FN_F11
+KEY_FN_F12
+KEY_FN_F2
+KEY_FN_F3
+KEY_FN_F4
+KEY_FN_F5
+KEY_FN_F6
+KEY_FN_F7
+KEY_FN_F8
+KEY_FN_F9
+KEY_FN_S
+KEY_FORWARD
+KEY_FORWARDMAIL
+KEY_FRAMEBACK
+KEY_FRAMEFORWARD
+KEY_FRONT
+KEY_G
+KEY_GAMES
+KEY_GOTO
+KEY_GRAPHICSEDITOR
+KEY_GRAVE
+KEY_GREEN
+KEY_H
+KEY_HANGEUL
+KEY_HANJA
+KEY_HELP
+KEY_HENKAN
+KEY_HIRAGANA
+KEY_HOME
+KEY_HOMEPAGE
+KEY_HP
+KEY_I
+KEY_INFO
+KEY_INSERT
+KEY_INS_LINE
+KEY_ISO
+KEY_J
+KEY_K
+KEY_KATAKANA
+KEY_KATAKANAHIRAGANA
+KEY_KBDILLUMDOWN
+KEY_KBDILLUMTOGGLE
+KEY_KBDILLUMUP
+KEY_KEYBOARD
+KEY_KP0
+KEY_KP1
+KEY_KP2
+KEY_KP3
+KEY_KP4
+KEY_KP5
+KEY_KP6
+KEY_KP7
+KEY_KP8
+KEY_KP9
+KEY_KPASTERISK
+KEY_KPCOMMA
+KEY_KPDOT
+KEY_KPENTER
+KEY_KPEQUAL
+KEY_KPJPCOMMA
+KEY_KPLEFTPAREN
+KEY_KPMINUS
+KEY_KPPLUS
+KEY_KPPLUSMINUS
+KEY_KPRIGHTPAREN
+KEY_KPSLASH
+KEY_L
+KEY_LANGUAGE
+KEY_LAST
+KEY_LEFT
+KEY_LEFTALT
+KEY_LEFTBRACE
+KEY_LEFTCTRL
+KEY_LEFTMETA
+KEY_LEFTSHIFT
+KEY_LINEFEED
+KEY_LIST
+KEY_LOGOFF
+KEY_M
+KEY_MACRO
+KEY_MAIL
+KEY_MAX
+KEY_MEDIA
+KEY_MEDIA_REPEAT
+KEY_MEMO
+KEY_MENU
+KEY_MESSENGER
+KEY_MHP
+KEY_MINUS
+KEY_MODE
+KEY_MOVE
+KEY_MP3
+KEY_MSDOS
+KEY_MUHENKAN
+KEY_MUTE
+KEY_N
+KEY_NEW
+KEY_NEWS
+KEY_NEXT
+KEY_NEXTSONG
+KEY_NUMERIC_0
+KEY_NUMERIC_1
+KEY_NUMERIC_2
+KEY_NUMERIC_3
+KEY_NUMERIC_4
+KEY_NUMERIC_5
+KEY_NUMERIC_6
+KEY_NUMERIC_7
+KEY_NUMERIC_8
+KEY_NUMERIC_9
+KEY_NUMERIC_POUND
+KEY_NUMERIC_STAR
+KEY_NUMLOCK
+KEY_O
+KEY_OK
+KEY_OPEN
+KEY_OPTION
+KEY_P
+KEY_PAGEDOWN
+KEY_PAGEUP
+KEY_PASTE
+KEY_PAUSE
+KEY_PAUSECD
+KEY_PC
+KEY_PHONE
+KEY_PLAY
+KEY_PLAYCD
+KEY_PLAYER
+KEY_PLAYPAUSE
+KEY_POWER
+KEY_POWER2
+KEY_PRESENTATION
+KEY_PREVIOUS
+KEY_PREVIOUSSONG
+KEY_PRINT
+KEY_PROG1
+KEY_PROG2
+KEY_PROG3
+KEY_PROG4
+KEY_PROGRAM
+KEY_PROPS
+KEY_PVR
+KEY_Q
+KEY_QUESTION
+KEY_R
+KEY_RADIO
+KEY_RECORD
+KEY_RED
+KEY_REDO
+KEY_REFRESH
+KEY_REPLY
+KEY_RESERVED
+KEY_RESTART
+KEY_REWIND
+KEY_RFKILL
+KEY_RIGHT
+KEY_RIGHTALT
+KEY_RIGHTBRACE
+KEY_RIGHTCTRL
+KEY_RIGHTMETA
+KEY_RIGHTSHIFT
+KEY_RO
+KEY_S
+KEY_SAT
+KEY_SAT2
+KEY_SAVE
+KEY_SCALE
+KEY_SCREEN
+KEY_SCROLLDOWN
+KEY_SCROLLLOCK
+KEY_SCROLLUP
+KEY_SEARCH
+KEY_SELECT
+KEY_SEMICOLON
+KEY_SEND
+KEY_SENDFILE
+KEY_SETUP
+KEY_SHOP
+KEY_SHUFFLE
+KEY_SLASH
+KEY_SLEEP
+KEY_SLOW
+KEY_SOUND
+KEY_SPACE
+KEY_SPELLCHECK
+KEY_SPORT
+KEY_SPREADSHEET
+KEY_STOP
+KEY_STOPCD
+KEY_SUBTITLE
+KEY_SUSPEND
+KEY_SWITCHVIDEOMODE
+KEY_SYSRQ
+KEY_T
+KEY_TAB
+KEY_TAPE
+KEY_TEEN
+KEY_TEXT
+KEY_TIME
+KEY_TITLE
+KEY_TUNER
+KEY_TV
+KEY_TV2
+KEY_TWEN
+KEY_U
+KEY_UNDO
+KEY_UNKNOWN
+KEY_UP
+KEY_UWB
+KEY_V
+KEY_VCR
+KEY_VCR2
+KEY_VENDOR
+KEY_VIDEO
+KEY_VIDEO_NEXT
+KEY_VIDEOPHONE
+KEY_VIDEO_PREV
+KEY_VOICEMAIL
+KEY_VOLUMEDOWN
+KEY_VOLUMEUP
+KEY_W
+KEY_WAKEUP
+KEY_WIMAX
+KEY_WLAN
+KEY_WORDPROCESSOR
+KEY_WPS_BUTTON
+KEY_WWW
+KEY_X
+KEY_XFER
+KEY_Y
+KEY_YELLOW
+KEY_YEN
+KEY_Z
+KEY_ZENKAKUHANKAKU
+KEY_ZOOM
+KEY_ZOOMIN
+KEY_ZOOMOUT
+KEY_ZOOMRESET
+
diff --git a/sapphire-remote-6.6/dkms.conf b/sapphire-remote-6.6/dkms.conf
new file mode 100644 (file)
index 0000000..7c4066e
--- /dev/null
@@ -0,0 +1,12 @@
+PACKAGE_VERSION="6.6"
+
+# Items below here should not have to change with each driver version
+PACKAGE_NAME="sapphire-remote"
+MAKE[0]="make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules"
+CLEAN="make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"
+
+BUILT_MODULE_NAME[0]="sapphire"
+DEST_MODULE_LOCATION[0]="/extra/"
+
+AUTOINSTALL=yes
+REMAKE_INITRD=no
diff --git a/sapphire-remote-6.6/extract_keydefs.sh b/sapphire-remote-6.6/extract_keydefs.sh
new file mode 100755 (executable)
index 0000000..865db6f
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Extract button and key definitions from sapphire.h and input.h:
+#
+
+function parse_sapphire_h(){
+       echo
+       echo "## buttons, macros, and other definitions extracted from $1:"
+       echo "##"
+       gawk '
+               ($2 == "BEGIN_PARSING"){c=1;next}
+               (c == 0){next}
+               /^      [XS]APPHIRE_([A-Z0-9_]+)\>([    ]+)= 0x/{printf "B[%s]=%s\n",$1,$3;next}
+               /^      KEY_MACRO_[0-7]\>([     ]+)= 0x/{printf "B[%s]=0x%08x\n",$1,strtonum($3)}
+               /^      KEY_([A-Z0-9_]+)\>([    ]+)= 0x/{printf "K[%s]=0x%08x\n",$1,strtonum($3);next}
+               /^      ([A-Z0-9_]+)\>([        ]+)= 0x/{printf "M[%s]=0x%08x\n",$1,strtonum($3);next}
+               /^[}];/{if (c == 1) nextfile}
+       ' < "$1"
+}
+
+function parse_input_h(){
+       echo
+       echo "## keycodes extracted from $1:"
+       echo "##"
+       gawk '  /^#define KEY_([A-Z0-9_]+)\>([  ]+)((0x[0-9a-fA-F]+)|([0-9]+))\>/{
+                       printf "K[%s]=0x%08x\n",$2,strtonum($3)}
+       ' < "$1" | grep -v KEY_RESERVED
+}
+
+parse_input_h "$1" && parse_sapphire_h "$2"
+
diff --git a/sapphire-remote-6.6/keymap.default b/sapphire-remote-6.6/keymap.default
new file mode 100644 (file)
index 0000000..ed0dcab
--- /dev/null
@@ -0,0 +1,100 @@
+#
+# Default keymap from sapphire.c v3.0.
+#
+# Edit this as you please, keeping in mind that spaces are NOT permitted
+# within each column.
+#
+#   Eg. this is INCORRECT:   KEY_F15 | ALT
+#   whereas this is good:    KEY_F15|ALT
+#         
+# Use the provided sapphire_keymap.sh script to install these
+# mappings at runtime in place of the driver's built-in mappings.
+# Keep your own copy of this file separate from the driver source,
+# so that subsequent changes in the driver mappings won't clobber
+# your own definitions here.
+#
+# As in the example above, "KeyCode" values can be combined with
+# any mix of these "modifier" keys:  SHIFT, ALT, CTRL, and/or META.
+#
+#  Eg.  SAPPHIRE_PLAY  KEY_P|CTRL      NO_REPEAT  ## Mythtv Play/Pause function
+#  Or:  SAPPHIRE_PLAY  KEY_P|CTRL|ALT  NO_REPEAT  ## something else
+#
+# The "SpecialOptions" field is used to indicate the desired auto-repeat rate
+# for each button individually.  The choices are:
+#
+#     NO_REPEAT   - the button will not auto-repeat when held down.
+#     SLOW_REPEAT - the button will repeat slowly.
+#     RAMP_REPEAT - the button will repeat slowly at first, but speed up if held down.
+#     LONGKEY     - no auto-repeat: special behaviour described below.
+#
+# The special value "LONGKEY" can be used in "SpecialOptions"
+# insted of an auto-repeat setting.  This causes a button on the remote
+# to have two separate functions, depending upon how quickly it is released.
+#
+# A short-press/release gets the regular "KeyCode" value, but if the button
+# is held down for one second or longer, then an alternate value can be send instead.
+# Just combine the desired alternate keycode with the LONGKEY tag.
+#
+# Eg.  SAPPHIRE_MUTE  KEY_F9  LONGKEY|KEY_F15  ## Mute (tap) or Audiosync (hold)
+#
+# ButtonName             KeyCode          SpecialOptions               ## Comments
+#
+SAPPHIRE_UP              KEY_UP           RAMP_REPEAT                  ## up    (CUSTOM)
+SAPPHIRE_DOWN            KEY_DOWN         RAMP_REPEAT                  ## down  (CUSTOM)
+SAPPHIRE_RIGHT           KEY_RIGHT        SLOW_REPEAT                  ## right
+SAPPHIRE_LEFT            KEY_LEFT         SLOW_REPEAT                  ## left
+SAPPHIRE_ENTEROK         KEY_ENTER        LONGKEY|KEY_F15              ## Select (tap) or Audiosync (hold)
+SAPPHIRE_BACK            KEY_ESC          SLOW_REPEAT                  ## Back
+SAPPHIRE_PLAY            KEY_P|CTRL       NO_REPEAT                    ## Play/Pause
+SAPPHIRE_PAUSE           KEY_P            NO_REPEAT                    ## Play/Pause
+SAPPHIRE_VOLUP           KEY_RIGHTBRACE   FAST_REPEAT                  ## Volume Up
+SAPPHIRE_VOLDOWN         KEY_LEFTBRACE    FAST_REPEAT                  ## Volume Down
+SAPPHIRE_CHUP            KEY_PAGEUP       SLOW_REPEAT                  ## channel up    (CUSTOM)
+SAPPHIRE_CHDOWN          KEY_PAGEDOWN     SLOW_REPEAT                  ## channel down  (CUSTOM)
+SAPPHIRE_MUTE            KEY_F9           LONGKEY|KEY_F15              ## Mute (tap) or Audiosync (hold)
+SAPPHIRE_RECORD          KEY_R            NO_REPEAT                    ## Record/Delete (CUSTOM)
+SAPPHIRE_FWD             KEY_DOT          NO_REPEAT                    ## FFwd
+SAPPHIRE_REW             KEY_COMMA        NO_REPEAT                    ## Rewind
+SAPPHIRE_ANGLE           KEY_W            NO_REPEAT                    ## Adjust Fill
+SAPPHIRE_SAP             KEY_W|CTRL       NO_REPEAT                    ## Toggle Aspect Ratio
+SAPPHIRE_DVDMENU         KEY_M            NO_REPEAT                    ## Menu
+SAPPHIRE_INFOEPG         KEY_I            NO_REPEAT                    ## Info
+SAPPHIRE_TAB             KEY_END          SLOW_REPEAT                  ## Commskip Fwd
+SAPPHIRE_BACKTAB         KEY_HOME         SLOW_REPEAT                  ## Commskip Rev
+SAPPHIRE_RADIO           KEY_F7|ALT       NO_REPEAT                    ## Signal Monitor
+SAPPHIRE_LASTCH          KEY_H            NO_REPEAT                    ## previous chan
+SAPPHIRE_LANGUAGE        KEY_EQUAL|SHIFT  NO_REPEAT                    ## Next Audio Track
+SAPPHIRE_TELETEXTCC      KEY_T            NO_REPEAT                    ## Toggle Closed-Caption
+SAPPHIRE_SUBTITLE        KEY_F17|ALT      NO_REPEAT                    ## Toggle subtitle (CUSTOM)
+SAPPHIRE_HOMEHOUSE       KEY_F18          NO_REPEAT                    ## jump to MainMenu (CUSTOM jumppoint)
+SAPPHIRE_BLUEVIDEOS      KEY_F14          NO_REPEAT                    ## MythVideo        (CUSTOM jumppoint)
+SAPPHIRE_LIVETV          KEY_F16          NO_REPEAT                    ## Program Guide    (CUSTOM jumppoint)
+SAPPHIRE_REDDVDVCD       KEY_F13          LONGKEY|KEY_F13|ALT          ## PlayDVD/DVDMenu (CUSTOM jumppoints)
+SAPPHIRE_YELLOWPICTURES  KEY_F14|ALT      NO_REPEAT                    ## Watch Recordings (CUSTOM jumppoint)
+SAPPHIRE_1               KEY_1            SLOW_REPEAT                  ## 1
+SAPPHIRE_2               KEY_2            SLOW_REPEAT                  ## 2
+SAPPHIRE_3               KEY_3            SLOW_REPEAT                  ## 3
+SAPPHIRE_4               KEY_4            SLOW_REPEAT                  ## 4
+SAPPHIRE_5               KEY_5            SLOW_REPEAT                  ## 5
+SAPPHIRE_6               KEY_6            SLOW_REPEAT                  ## 6
+SAPPHIRE_7               KEY_7            SLOW_REPEAT                  ## 7
+SAPPHIRE_8               KEY_8            SLOW_REPEAT                  ## 8
+SAPPHIRE_9               KEY_9            SLOW_REPEAT                  ## 9
+SAPPHIRE_0               KEY_0            SLOW_REPEAT                  ## 0
+SAPPHIRE_STOP            KEY_MINUS        LONGKEY|KEY_MINUS|SHIFT      ## dash/underscore symbols
+SAPPHIRE_POWER           KEY_F2           NO_REPEAT                    ## start frontend (CUSTOM desktop script)
+SAPPHIRE_CLEAR           KEY_MACRO_0      NO_REPEAT                    ## Adjust audiosync by 90msecs (see below)
+SAPPHIRE_GREENMUSIC      KEY_A            NO_REPEAT                    ## Adjust time stretch
+
+#
+# Now for the macro definitions: each macro sends a list of up to eight keycodes:
+#
+KEY_MACRO_0    KEY_F15 KEY_DOWN KEY_LEFT   ## Adjust AudioSync by +90msecs
+KEY_MACRO_1
+KEY_MACRO_2
+KEY_MACRO_2
+KEY_MACRO_3
+KEY_MACRO_4
+KEY_MACRO_5
+KEY_MACRO_6
+KEY_MACRO_7
diff --git a/sapphire-remote-6.6/sapphire.c b/sapphire-remote-6.6/sapphire.c
new file mode 100644 (file)
index 0000000..b7414b0
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ * sapphire.c
+ *
+ * Copyright Mark Lord <mlord@pobox.com>, 2012-2015.
+ * http://rtr.ca/sapphire_remote/
+ *
+ * This is a HID driver for the TopSeed Cyberlink receiver
+ * when paired with the "Sapphire TheatriX" remote control.
+ *
+ * This replaces the in-kernel "hid-topseed" driver,
+ * and is tailored specifically for use with Mythtv
+ * without need of any external configuration or LIRC etc.
+ *
+ * Buttons can have variable repeat rates, distinguish between short
+ * and long presses, and can send macros instead of just a single keycode.
+ *
+ * Wake-from-suspend works by pressing the remote's "Power" button,
+ * after correctly configuring /proc/acpi/wakeup before suspending.
+ *
+ * User-defined keymaps and macros can be (re)configured on the fly
+ * via a /proc/ interface, using the supplied sapphire_keymap.sh script.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; specifically version 2 of the License (GPLv2).
+ */
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb.h>
+#include "sapphire.h"
+
+#define SAPPHIRE_NAME          "sapphire"
+#define SAPPHIRE_VERSION       "6.6"
+
+/*
+ * "LONGKEY":  Any button can be used to generate a second, different keycode,
+ * by setting the "repeats" value to "LONGKEY | KEY_XXX" for the second keycode.
+ * When that button is then tapped (quick press/release), the regular keycode is sent.
+ * But when the button is pressed/held for one second, the second keycode is sent
+ * instead of the regular keycode. This allows buttons to have dual functionalty,
+ * to solve some sticky issues.
+ *
+ * Eg. by default below, the MUTE button is set up this way: a short tap of MUTE
+ * sends the "Mute" command, but holding it down longer will instead activate
+ * the "Adjust Audio Sync" pop-up.  Very, VERY handy, that.
+ *
+ * EDIT the "keycode" and "repeats" columns below to customize the keymap to your needs:
+ */
+#define SAPPHIRE_NUM_BUTTONS   46   /* The Sapphire remote sends 46 unique button codes */
+#define SAPPHIRE_KEYMAP_SIZE   (2 * SAPPHIRE_NUM_BUTTONS)  /* Extra room for a full XAPPHIRE_* set */
+static struct sapphire_map {
+       unsigned int button;   /* raw data from sensor */
+       unsigned int keycode;  /* keyboard value to pass to mythtv, or "0" for nothing */
+       unsigned int repeats;  /* auto-repeat setting */
+} sapphire_keymap[SAPPHIRE_KEYMAP_SIZE] = {
+  /*   button                 keycode          repeats                       mythtv action  */
+  /* ==========               ========         ===========                   =============  */
+    {SAPPHIRE_UP            , KEY_UP         , RAMP_REPEAT             }, /* up    (CUSTOM) */
+    {SAPPHIRE_DOWN          , KEY_DOWN       , RAMP_REPEAT             }, /* down  (CUSTOM) */
+    {SAPPHIRE_RIGHT         , KEY_RIGHT      , SLOW_REPEAT             }, /* right          */
+    {SAPPHIRE_LEFT          , KEY_LEFT       , SLOW_REPEAT             }, /* left           */
+    {SAPPHIRE_ENTEROK       , KEY_ENTER      , LONGKEY|KEY_F15         }, /* Select (tap) or Audiosync (hold) */
+    {SAPPHIRE_BACK          , KEY_ESC        , SLOW_REPEAT             }, /* Back           */
+    {SAPPHIRE_PLAY          , KEY_P|CTRL     , NO_REPEAT               }, /* Play/Pause     */
+    {SAPPHIRE_PAUSE         , KEY_P          , NO_REPEAT               }, /* Play/Pause     */
+    {SAPPHIRE_VOLUP         , KEY_RIGHTBRACE , FAST_REPEAT             }, /* Volume Up      */
+    {SAPPHIRE_VOLDOWN       , KEY_LEFTBRACE  , FAST_REPEAT             }, /* Volume Down    */
+    {SAPPHIRE_CHUP          , KEY_PAGEUP     , SLOW_REPEAT             }, /* channel up    (CUSTOM) */
+    {SAPPHIRE_CHDOWN        , KEY_PAGEDOWN   , SLOW_REPEAT             }, /* channel down  (CUSTOM) */
+    {SAPPHIRE_MUTE          , KEY_F9         , LONGKEY|KEY_F15         },  /* Mute (tap) or Audiosync (hold) */
+    {SAPPHIRE_RECORD        , KEY_R          , NO_REPEAT               }, /* Record/Delete (CUSTOM) */
+    {SAPPHIRE_FWD           , KEY_DOT        , NO_REPEAT               }, /* FFwd           */
+    {SAPPHIRE_REW           , KEY_COMMA      , NO_REPEAT               }, /* Rewind         */
+    {SAPPHIRE_ANGLE         , KEY_W          , NO_REPEAT               }, /* Adjust Fill    */
+    {SAPPHIRE_SAP           , KEY_W|CTRL     , NO_REPEAT               }, /* Toggle Aspect Ratio */
+    {SAPPHIRE_DVDMENU       , KEY_M          , LONGKEY|KEY_F13|ALT     }, /* Menu/DVDMenu (CUSTOM) */
+    {SAPPHIRE_INFOEPG       , KEY_I          , NO_REPEAT               }, /* Info           */
+    {SAPPHIRE_TAB           , KEY_END        , SLOW_REPEAT             }, /* Commskip Fwd   */
+    {SAPPHIRE_BACKTAB       , KEY_HOME       , SLOW_REPEAT             }, /* Commskip Rev   */
+    {SAPPHIRE_RADIO         , KEY_F7|ALT     , NO_REPEAT               }, /* Signal Monitor */
+    {SAPPHIRE_LASTCH        , KEY_H          , NO_REPEAT               }, /* previous chan  */
+    {SAPPHIRE_LANGUAGE      , KEY_EQUAL|SHIFT, NO_REPEAT               }, /* Next Audio Track */
+    {SAPPHIRE_TELETEXTCC    , KEY_T          , NO_REPEAT               }, /* Toggle Closed-Caption */
+    {SAPPHIRE_SUBTITLE      , KEY_F17|ALT    , NO_REPEAT               }, /* Toggle subtitle (CUSTOM) */
+    {SAPPHIRE_HOMEHOUSE     , KEY_F18        , NO_REPEAT               }, /* jump to MainMenu (CUSTOM jumppoint) */
+    {SAPPHIRE_BLUEVIDEOS    , KEY_F14        , NO_REPEAT               }, /* MythVideo        (CUSTOM jumppoint) */
+    {SAPPHIRE_LIVETV        , KEY_F16        , NO_REPEAT               }, /* Program Guide    (CUSTOM jumppoint) */
+    {SAPPHIRE_REDDVDVCD     , KEY_F13        , LONGKEY|KEY_F13|ALT     }, /* PlayDVD/DVDMenu  (CUSTOM jumppoints) */
+    {SAPPHIRE_YELLOWPICTURES, KEY_F14|ALT    , NO_REPEAT               }, /* Watch Recordings (CUSTOM jumppoint) */
+    {SAPPHIRE_1             , KEY_1          , SLOW_REPEAT             }, /* 1              */
+    {SAPPHIRE_2             , KEY_2          , SLOW_REPEAT             }, /* 2              */
+    {SAPPHIRE_3             , KEY_3          , SLOW_REPEAT             }, /* 3              */
+    {SAPPHIRE_4             , KEY_4          , SLOW_REPEAT             }, /* 4              */
+    {SAPPHIRE_5             , KEY_5          , SLOW_REPEAT             }, /* 5              */
+    {SAPPHIRE_6             , KEY_6          , SLOW_REPEAT             }, /* 6              */
+    {SAPPHIRE_7             , KEY_7          , SLOW_REPEAT             }, /* 7              */
+    {SAPPHIRE_8             , KEY_8          , SLOW_REPEAT             }, /* 8              */
+    {SAPPHIRE_9             , KEY_9          , SLOW_REPEAT             }, /* 9              */
+    {SAPPHIRE_0             , KEY_0          , SLOW_REPEAT             }, /* 0              */
+    {SAPPHIRE_STOP          , KEY_MINUS      , LONGKEY|KEY_MINUS|SHIFT }, /* dash/underscore symbols */
+    {SAPPHIRE_POWER         , KEY_F2         , NO_REPEAT               }, /* start frontend (CUSTOM desktop script) */
+    {SAPPHIRE_CLEAR         , KEY_MACRO_0    , NO_REPEAT               }, /* Adjust audiosync by 90msecs */
+    {SAPPHIRE_GREENMUSIC    , KEY_A          , NO_REPEAT               }, /* Adjust time stretch */
+};
+
+/*
+ * Up to 8 key "macros" can be defined here.
+ * These are mapped using the MACRO_[0-7] enums above,
+ * and each can consist of up to 8 KEY_* values,
+ * with zeros for any trailing unused values.
+ * The special KEY_DELAY can be used anywhere inside a macro
+ * to inject a half-second pause before continuing.
+ */
+static unsigned int sapphire_macros[8][8] = {
+       /* KEY_MACRO_0 */ {KEY_F15,KEY_DOWN,KEY_LEFT,0,},  /* adjust audiosync by 90msec */
+       /* KEY_MACRO_1 */ {0,},
+       /* KEY_MACRO_2 */ {0,},
+       /* KEY_MACRO_3 */ {0,},
+       /* KEY_MACRO_4 */ {0,},
+       /* KEY_MACRO_5 */ {0,},
+       /* KEY_MACRO_6 */ {0,},
+       /* KEY_MACRO_7 */ {0,}
+};
+
+static struct sapphire_dev {
+       spinlock_t              lock;
+       struct input_dev        *idev;
+       struct timer_list       key_timer;
+       u64                     last_event;
+       unsigned long           delay_end;
+       unsigned long           next_wakeup;
+       unsigned int            ready;
+       unsigned int            event_count;
+       unsigned int            down_key;       /* button currently being pressed */
+       unsigned int            next_repeat;    /* repeat interval for down_key */
+       unsigned int            last_key;
+       unsigned int            dualkey_short;  /* dual-function button "short press" value */
+       unsigned int            dualkey_long;   /* dual-function button "long press" value */
+       unsigned int            *delayed_macro; /* macro to be resumed after a 1/2 second delay */
+       unsigned int            delayed_next;   /* next button within the macro to be resumed */
+       unsigned int            raw_key;        /* a completely untranslated button being pressed */
+} sapphire_dev, *dev = &sapphire_dev;
+
+enum {
+       RAMPING         = 0x80000000,           /* flag to indicate current dev->down_key is using RAMP_REPEAT */ 
+       RAMPING_RATE    = HZ / 40,              /* amount to speed up by after each repeat */
+       RAMPING_MAX     = HZ / 10,              /* max repeats/sec when RAMPING */
+       MODIFIERS       = CTRL|SHIFT|ALT|META,  /* convenient mask of all modifier keys */
+       PRESSED         = 1,                    /* for use with input_event() */
+       RELEASED        = 0,                    /* for use with input_event() */
+};
+
+static unsigned int ramping_rate = RAMPING_RATE;
+static unsigned int ramping_max  = RAMPING_MAX;
+
+/*
+ * Translate a [XS]APPHIRE_ "button" into a KEY_ "keycode" or "macro".
+ */
+static unsigned int sapphire_remap(unsigned int button, unsigned int *repeats)
+{
+       struct sapphire_map *map;
+
+       for (map = sapphire_keymap; map != (sapphire_keymap + SAPPHIRE_KEYMAP_SIZE); ++map) {
+               if (map->button == button && map->keycode) {
+                       *repeats = map->repeats;
+                       return map->keycode;
+               }
+       }
+       *repeats = NO_REPEAT;
+       return 0;
+}
+
+/*
+ * Remember the last key pressed for later retrieval from /proc/sapphire
+ */
+static void sapphire_save_key(unsigned int key)
+{
+       if (key)
+               dev->last_key = key;
+       dev->event_count++;
+       dev->last_event = get_jiffies_64();
+}
+
+/*
+ * Send "modifier" (CTRL, SHIFT, ALT, META) press/release events to the Linux input system.
+ */
+static void sapphire_send_modifiers(unsigned int modifiers, int pressed_released)
+{
+       if (modifiers & CTRL)
+               input_event(dev->idev, EV_KEY, KEY_LEFTCTRL,  pressed_released);
+       if (modifiers & SHIFT)
+               input_event(dev->idev, EV_KEY, KEY_LEFTSHIFT, pressed_released);
+       if (modifiers & ALT)
+               input_event(dev->idev, EV_KEY, KEY_LEFTALT,   pressed_released);
+       if (modifiers & META)
+               input_event(dev->idev, EV_KEY, KEY_LEFTMETA,  pressed_released);
+}
+
+/*
+ * Start/modify the keypress/repeat timer:
+ */
+static void sapphire_set_timer (unsigned long timeout)
+{
+       if (timeout)
+               dev->next_wakeup = jiffies + timeout;
+       else if (!time_before(jiffies, dev->next_wakeup))
+               dev->next_wakeup = jiffies + 2;
+       mod_timer(&dev->key_timer, dev->next_wakeup);
+}
+
+static void sapphire_send_key(unsigned int key, unsigned int repeat_delay);
+
+static int sapphire_send_macro (unsigned int *macro, unsigned int next)
+{
+       unsigned int i;
+       for (i = next; i <= 7 && macro[i]; ++i) {
+               unsigned int key = macro[i];
+               if (key == KEY_DELAY) {
+                       if (i < 7 && macro[i + 1]) {
+                               dev->delayed_macro = macro;
+                               dev->delayed_next = i + 1;
+                               sapphire_set_timer(HZ / 2);
+                               return 1;
+                       }
+                       break;
+               }
+               sapphire_send_key(key, NO_REPEAT);
+       }
+       return 0;
+}
+
+/*
+ * Send a translated key/macro to the Linux input system.
+ */
+static void sapphire_send_key(unsigned int key, unsigned int repeat_delay)
+{
+       if (!key)
+               return;
+       sapphire_save_key(key);
+       if ((key & KEY_MACRO_0) == KEY_MACRO_0) {
+               unsigned int n = key & (KEY_MACRO_0 ^ KEY_MACRO_7);
+               if (sapphire_send_macro(sapphire_macros[n], 0))
+                       return;
+       } else {
+               unsigned int modifiers = key & MODIFIERS;
+               if (modifiers) {
+                       key &= ~MODIFIERS;
+                       sapphire_send_modifiers(modifiers, PRESSED);
+               }
+               input_event(dev->idev, EV_KEY, key, PRESSED);
+               input_event(dev->idev, EV_KEY, key, RELEASED);
+               if (modifiers)
+                       sapphire_send_modifiers(modifiers, RELEASED);
+               input_sync(dev->idev);
+       }
+       if (repeat_delay)
+               sapphire_set_timer(repeat_delay);
+}
+
+/*
+ * Timer callback function, used to handle repeats and "LONGKEYs".
+ */
+static void sapphire_key_timeout(unsigned long data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->ready = 1;
+       /*
+        * FIXME: not sure if this can ever happen for real.
+        *
+        * In theory, we could get blocked on the spin_lock_irqsave() above
+        * while another part of the driver is busy modifying the timer value
+        * in response to a new button event.  This could lead to us handling
+        * the new event immediately rather than after the newly set timeout.
+        * Guard against it by verifying that "next_wakeup" has really arrived.
+        */
+       if (time_before(jiffies, dev->next_wakeup)) {
+               /* Spinlock race on an old timer value; skip this wakeup. */
+               printk(KERN_INFO "%s: workaround activated: jiffies=%lu wakeup=%lu\n", __func__, jiffies, dev->next_wakeup);
+               sapphire_set_timer(0);  /* reset the timer with same next_wakeup value */
+       } else if (dev->delayed_macro) {
+               unsigned int *macro = dev->delayed_macro;
+               dev->delayed_macro = NULL;
+               sapphire_send_macro(macro, dev->delayed_next);
+       } else if (dev->down_key) {
+               unsigned int next_repeat = dev->next_repeat & ~RAMPING;
+               if (dev->next_repeat & RAMPING) {
+                       /* Gradually ramp-up the repeat rate for this key */
+                       if (next_repeat > ramping_max) {
+                               next_repeat -= ramping_rate;
+                               if (next_repeat < ramping_max)
+                                       next_repeat = ramping_max;
+                       }
+                       dev->next_repeat = next_repeat | RAMPING;
+               }
+               sapphire_send_key(dev->down_key, next_repeat);
+       } else if (dev->dualkey_short) {
+               sapphire_send_key(dev->dualkey_long, NO_REPEAT);
+               dev->dualkey_long = dev->dualkey_short = 0;
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Pass along a button press event, setting up the repeat mechanism appropriately.
+ */
+static void sapphire_handle_key (unsigned int key, unsigned int repeats)
+{
+       unsigned int repeat_delay = NO_REPEAT, next_repeat = 0;
+
+       switch (repeats) {
+               case RAWKEY:
+                       del_timer(&dev->key_timer);
+                       input_event(dev->idev, EV_KEY, key, PRESSED);
+                       input_sync(dev->idev);
+                       dev->raw_key = key;
+                       return;
+               case SLOW_REPEAT:
+                       repeat_delay = HZ/2;
+                       next_repeat  = HZ/SLOW_REPEAT;
+                       break;
+               case FAST_REPEAT:
+                       repeat_delay = HZ/3;
+                       next_repeat  = HZ/FAST_REPEAT;
+                       break;
+               case RAMP_REPEAT:
+                       repeat_delay = (HZ/2) - (HZ/6);
+                       next_repeat  = (HZ/3) | RAMPING;
+                       break;
+               default:  /* LONGKEY or NO_REPEAT */
+                       if (repeats & LONGKEY) {
+                               /*
+                                * We don't know if it's a short or long press yet.
+                                * Start the key_timer to sort things out.
+                                */
+                               dev->dualkey_short = key;
+                               dev->dualkey_long  = repeats & ~LONGKEY;
+                               sapphire_set_timer(HZ);
+                               dev->delay_end = dev->next_wakeup;
+                               return;
+                       }
+       }
+       sapphire_send_key(key, repeat_delay);
+       if (next_repeat ) {
+               dev->down_key    = key;
+               dev->next_repeat = next_repeat;
+       }
+}
+
+/*
+ * Handle a low-level press or release event,
+ */
+static void sapphire_event(u32 data)
+{
+       /* Treat each new event as a "release" for any previous "press" of a button */
+       if (dev->down_key || dev->dualkey_short) {
+               dev->down_key = 0;
+               del_timer(&dev->key_timer);
+               if (dev->dualkey_short) {
+                       if (time_before(jiffies, dev->delay_end))
+                               sapphire_send_key(dev->dualkey_short, NO_REPEAT);
+                       else
+                               sapphire_send_key(dev->dualkey_long,  NO_REPEAT);
+                       dev->dualkey_long = dev->dualkey_short = 0;
+               }
+       }
+       if (dev->raw_key) {
+               input_event(dev->idev, EV_KEY, dev->raw_key, RELEASED);
+               input_sync(dev->idev);
+               dev->raw_key = 0;
+       }
+       /* Key "press" events are > 0x10; nothing required here for "release" events (<= 0x10) */
+       if (data > 0x10) {  /* key "press" event? */
+               unsigned int repeats, key = sapphire_remap(data, &repeats);
+               if (key) {
+                       sapphire_handle_key(key, repeats);
+                       return;
+               }
+               printk(KERN_INFO "%s: unmapped button: 0x%08x\n", __func__, data);
+       }
+       sapphire_save_key(0);  /* timestamp the event in /proc/, for shutdown timing.. */
+}
+
+/*
+ * This gets called each time the IR receiver "receives" something from a remote control.
+ * The "size" of "raw_data" bytes received varies depending upon the button pressed,
+ * but we need only the first four bytes to uniquely distinguish all buttons from each other.
+ * The exceptions are the "OK" and "Enter" buttons, which send 100% identical data,
+ * so we cannot distinguish those from each other.  All other buttons send unique data.
+ */
+static int sapphire_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *raw_data, int size)
+{
+       unsigned long flags;
+       u32 data = 0;
+       int i;
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT))
+               return 0;
+       if (dev->ready) {
+               if (size > 4)   /* Use only the first four bytes or less */
+                       size = 4;
+               for (i = size; i > 0; )
+                       data = (data << 8) | raw_data[--i];
+               spin_lock_irqsave(&dev->lock, flags);
+               sapphire_event(data);
+               spin_unlock_irqrestore(&dev->lock, flags);
+       }
+       return -1;  /* event was handled (used to return "1" here) */
+}
+
+/*
+ * This hook provides a means for other drivers to feed
+ * their button press/release events through this driver,
+ * to take advantage of sapphire's macros, variable repeats,
+ * long/short press mappings, modifier keys (CTRL,ALT,SHIFT,META),
+ * and the /proc/sapphire interface.
+ *
+ * The incoming data must be pre-translated to sapphire buttons
+ * by the caller, sending non-zero [XS]APPHIRE_* codes for presses,
+ * and zero for button release events.
+ *
+ * There is a "glue.c" driver available demonstrating this,
+ * in conjunction with a tiny patch to cx88-input.c.
+ * This allows cx88 remote controls to feed through sapphire.c
+ * and behave similarly to the native Sapphire remote-control.
+ */
+void sapphire_relay (u32 data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       sapphire_event(data);
+       spin_unlock_irqrestore(&dev->lock, flags);
+}
+EXPORT_SYMBOL(sapphire_relay);
+
+/*
+ * Parse and save a single macro definition written via /proc/
+ */
+static int sapphire_store_macro(char *buf)
+{
+       unsigned long flags;
+       unsigned int vals[8] = {0,}, macro, n;
+       char excess;
+       int count;
+
+       count = sscanf(buf, "%x %x %x %x %x %x %x %x %x %c", &macro,
+                       &vals[0], &vals[1], &vals[2], &vals[3],
+                       &vals[4], &vals[5], &vals[6], &vals[7], &excess);
+       if (count < 1 || count > 9 || (macro & KEY_MACRO_0) != KEY_MACRO_0)
+               return -EINVAL;
+       spin_lock_irqsave(&dev->lock, flags);
+       n = macro & (KEY_MACRO_0 ^ KEY_MACRO_7);
+       memcpy(sapphire_macros[n], vals, sizeof(vals));
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return 0;
+}
+
+/*
+ * Parse and save a single button mapping written via /proc/
+ */
+static int sapphire_store_keymap(struct sapphire_map *map, char *buf)
+{
+       unsigned long flags;
+       unsigned int vals[3] = {0,};
+       char excess;
+
+       if (3 != sscanf(buf, "%x %x %x %c", &vals[0], &vals[1], &vals[2], &excess))
+               return -EINVAL;
+       spin_lock_irqsave(&dev->lock, flags);
+       map->button  = vals[0];
+       map->keycode = vals[1];
+       map->repeats = vals[2];
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return 0;
+}
+
+/*
+ * Provide useful info via /proc/
+ */
+static int sapphire_oldproc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data)
+{
+       u64 last_event, now, elapsed;
+       unsigned long flags;
+       unsigned int len, last_key = 0, secs = ~0, event_count;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       event_count = dev->event_count;
+       last_event  = dev->last_event;
+       last_key    = dev->last_key;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       now = get_jiffies_64();
+       elapsed = now - last_event;
+       secs = ((unsigned int)elapsed) / HZ;
+       len  = sprintf(buf, "event_count %u\nlast_key 0x%08x\nelapsed_secs %u\n", event_count, last_key, secs);
+       *eof = 1;
+       return len;
+}
+
+/*
+ * Handles writes to /proc/ for altering key/macro mappings on the fly,
+ * and also for injecting keycodes from scripts etc.
+ */
+static int sapphire_oldproc_write(struct file *file, const char __user *ubuf, unsigned long count, void *data)
+{
+       static const char SENDKEY[] = "SENDKEY ";
+       static const char REPEAT_RATE[] = "REPEAT_RATE ";
+       unsigned int len = count, val, ret = 0;
+       u8 *buf = kmalloc(len + 2, GFP_KERNEL), *line, *eol;
+       struct sapphire_map *map;
+
+       if (!buf)
+               return -ENOMEM;
+       if (copy_from_user(buf, ubuf, len)) {
+               kfree(buf);
+               return -EFAULT;
+       }
+       if (len == 0 || buf[len - 1] != '\n')
+               buf[len++] = '\n';
+       buf[len++] = '\0';
+       for (line = buf; line[0]; ) {
+               int sendkey = 0;
+               for (eol = line; *eol != '\n'; ++eol);
+               *eol = '\0';
+               if (0 == strncmp(line, SENDKEY, sizeof(SENDKEY)-1)) {
+                       line += sizeof(SENDKEY)-1;
+                       sendkey = 1;
+               } else if (0 == strncmp(line, REPEAT_RATE, sizeof(REPEAT_RATE)-1)) {
+                       unsigned int rate, max;
+                       line += sizeof(REPEAT_RATE)-1;
+                       if (2 != sscanf(line, "%u %u", &rate, &max)) {
+                               ret = -EINVAL;
+                               break;
+                       }
+                       ramping_rate = HZ / rate;
+                       ramping_max  = HZ / max;
+                       goto next_line;
+               }
+               if (1 != sscanf(line, "%x", &val)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (sendkey) {
+                       unsigned long flags;
+                       spin_lock_irqsave(&dev->lock, flags);
+                       sapphire_send_key(val, NO_REPEAT);
+                       spin_unlock_irqrestore(&dev->lock, flags);
+               } else {
+                       ret = sapphire_store_macro(line);
+                       if (ret) {
+                               ret = -EINVAL;
+                               for (map = sapphire_keymap; map != (sapphire_keymap + SAPPHIRE_KEYMAP_SIZE); ++map) {
+                                       if (!map->button || map->button == val) {
+                                               ret = sapphire_store_keymap(map, line);
+                                               break;
+                                       }
+                               }
+                               if (ret)
+                                       break;
+                       }
+               }
+next_line:
+               line = eol + 1;
+       }
+       if (ret && line)
+               printk(KERN_ERR "%s: parse error: \"%s\"\n", __func__, line);
+       kfree(buf);
+       return ret ? ret : count;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0))
+
+static void sapphire_create_proc_entry (void)
+{
+       static struct proc_dir_entry *entry;
+
+       entry = create_proc_entry(SAPPHIRE_NAME, S_IFREG|S_IRUGO|S_IWUGO, NULL);
+       if (entry) {
+               entry->write_proc = sapphire_oldproc_write;
+               entry->read_proc  = sapphire_oldproc_read;
+       }
+}
+
+#else /* LINUX_VERSION_CODE */
+
+static ssize_t sapphire_newproc_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+       return sapphire_oldproc_write(file, buffer, count, PDE_DATA(file_inode(file)));
+}
+
+static ssize_t sapphire_newproc_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+       unsigned char *kbuf = (void *)__get_free_page(GFP_KERNEL);
+       ssize_t len;
+       int eof = 0;
+
+       if (!kbuf)
+               return -ENOMEM;
+       len = sapphire_oldproc_read(kbuf, NULL, 0, PAGE_SIZE - 1, &eof, PDE_DATA(file_inode(file)));
+       len = simple_read_from_buffer(buffer, count, ppos, kbuf, len);
+       free_page((long)kbuf);
+       return len;
+}
+
+static const struct file_operations sapphire_newproc_fops = {
+       .read = sapphire_newproc_read,
+       .write = sapphire_newproc_write,
+       .llseek = default_llseek,
+};
+
+static void sapphire_create_proc_entry (void)
+{
+       static struct proc_dir_entry *entry;
+       entry = proc_create_data(SAPPHIRE_NAME, S_IFREG|S_IRUGO|S_IWUGO, NULL, &sapphire_newproc_fops, dev);
+}
+
+#endif /* LINUX_VERSION_CODE */
+
+/*
+ * Invoked by the kernel each time one of our IR receivers is plugged into a USB port.
+ * We can/do handle multiple receivers, but there's no point to ever have more than one,
+ * because the receiver hardware is not keyed to a specific transmitter (remote control).
+ *
+ * If buttons are pressed before probe is run, the driver sometimes receives
+ * a single PRESS event at startup, with no corresponding RELEASE event.
+ * This is bad.  We prevent the stray presses by ignoring all input from the device
+ * for one second after probe, and then set ready=1 to enable normal processing.
+ */
+static int sapphire_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret = hid_parse(hdev);
+       if (ret)
+               return ret;
+               ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               printk(KERN_INFO SAPPHIRE_NAME ": hw_start failed, err=%d\n", ret);
+       } else {
+               /* Eat spurious data from the device for one second after connect */
+               unsigned long flags;
+               spin_lock_irqsave(&dev->lock, flags);
+               sapphire_event(0);      /* clear current downkey state */
+               dev->ready = 0;         /* ignore input until the timer fires */
+               sapphire_set_timer(HZ);
+               spin_unlock_irqrestore(&dev->lock, flags);
+       }
+       return ret;
+}
+
+/*
+ * Invoked by the kernel each time one of our IR receivers is unplugged from a USB port.
+ */
+static void sapphire_remove(struct hid_device *hdev)
+{
+       /*
+        * We are supposed to do hid_hw_stop() here.
+        * But some Ubuntu kernels (eg. 3.11.x, 3.2.x) totally kill the device
+        * when doing this, such that even hid_hw_start() won't revive it.
+        * Other kernels handle it much better, but picking and choosing
+        * can be very problematic.  So..
+        */
+       hid_hw_stop(hdev);
+}
+
+#ifdef CONFIG_PM
+/*
+ * The IR receiver becomes non-responsive when subjected to USB autosuspend.
+ * This is disabled by default at probe() time, but system scripts
+ * (particularly on suspend/resume) may modify the setting,
+ * and the result is that the IR receiver could still suffer autosuspend.
+ *
+ * So.. when the system does try it, by calling sapphire_suspend() below,
+ * we reject the attempt (-EBUSY) and also reset the autosuspend setting
+ * back to "on" again.  Unfortunately, this cannot be done directly
+ * from sapphire_suspend() due to spinlock recursion in the PM code,
+ * so we schedule a worker to do it from a less-encumbered state.
+ */
+struct sapphire_pm_work {
+       struct work_struct work;
+       struct usb_device *udev;
+};
+
+static void sapphire_disable_usb_autosuspend(struct work_struct *work)
+{
+       struct sapphire_pm_work *w = container_of(work, struct sapphire_pm_work, work);
+
+       usb_disable_autosuspend(w->udev);
+       kfree(w);
+       printk(KERN_INFO SAPPHIRE_NAME ": disabled USB autosuspend\n");
+}
+
+static void sapphire_schedule_pm_work(struct hid_device *hdev)
+{
+       struct sapphire_pm_work *w = kmalloc(sizeof(struct sapphire_pm_work), GFP_ATOMIC);
+       if (w) {
+               w->udev = container_of(hdev->dev.parent->parent, struct usb_device, dev);
+               INIT_WORK(&w->work, sapphire_disable_usb_autosuspend);
+               schedule_work(&w->work);
+       }
+}
+
+static int sapphire_suspend(struct hid_device *hdev, pm_message_t message)
+{
+       if (PMSG_IS_AUTO(message)) {
+               sapphire_schedule_pm_work(hdev);  /* prevent future attempts */
+               return -EBUSY;  /* prevent _this_ USB autosuspend attempt */
+       }
+       return 0;
+}
+
+static int sapphire_resume(struct hid_device *hdev)
+{
+        return 0;
+}
+
+#endif
+
+/*
+ * Structures used to associate this driver with specific USB device types.
+ */
+static const struct hid_device_id sapphire_devices[] = {
+       {HID_USB_DEVICE(0x0766,0x0204)},
+       {}
+};
+MODULE_DEVICE_TABLE(hid, sapphire_devices);
+static struct hid_driver sapphire_driver = {
+       .name           = SAPPHIRE_NAME,
+       .id_table       = sapphire_devices,
+       .raw_event      = sapphire_raw_event,
+       .probe          = sapphire_probe,
+       .remove         = sapphire_remove,
+#ifdef CONFIG_PM
+       .suspend        = sapphire_suspend,
+       .resume         = sapphire_resume,
+#endif
+};
+
+/*
+ * We don't use the input_dev provided by the hid layer,
+ * because (1) it provides two of them (mouse & keyboard),
+ * and (2) we need an input_dev even if no receiver is plugged in.
+ * Here we allocate a single global input_dev for use by the driver.
+ */
+static int sapphire_create (void)
+{
+       int keycode, err;
+
+       memset(dev, 0, sizeof(*dev));   /* paranoia */
+       spin_lock_init(&dev->lock);
+       init_timer(&dev->key_timer);
+       dev->key_timer.function = sapphire_key_timeout;
+       dev->key_timer.data     = (unsigned long)dev;
+
+       dev->idev = input_allocate_device();
+       if (!dev->idev) {
+               printk(KERN_ERR "%s: input_allocate_device() failed\n", __func__);
+               return -ENOMEM;
+       }
+       dev->idev->name = SAPPHIRE_NAME;
+       dev->idev->phys = "none";
+       dev->idev->evbit[0] = BIT_MASK(EV_KEY);
+       for (keycode = 1; keycode < KEY_MAX; ++keycode)
+               __set_bit(keycode, dev->idev->keybit); 
+       err = input_register_device(dev->idev);
+       if (err) {
+               printk(KERN_ERR "%s: input_register_device() failed, ret=%d\n", __func__, err);
+               input_free_device(dev->idev);
+               dev->idev = NULL;
+               return err;
+       }
+       return 0;
+}
+
+/*
+ * This is the inverse of sapphire_create_inputdev().
+ */
+static void sapphire_destroy (void)
+{
+       del_timer_sync(&dev->key_timer);
+       input_unregister_device(dev->idev);
+       input_free_device(dev->idev);
+}
+
+/*
+ * Invoked by the kernel when this module is loaded.
+ */
+static int __init sapphire_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "%s: " SAPPHIRE_NAME " remote control driver v%s\n", __func__, SAPPHIRE_VERSION);
+       ret = sapphire_create();
+       if (ret)
+               return ret;
+       dev->last_event = get_jiffies_64();
+       ret = hid_register_driver(&sapphire_driver);
+       if (ret)
+               sapphire_destroy();
+       else
+               sapphire_create_proc_entry();
+       return ret;
+}
+
+/*
+ * Invoked by the kernel when this module is removed/unloaded.
+ */
+static void __exit sapphire_exit(void)
+{
+       remove_proc_entry(SAPPHIRE_NAME, NULL);
+       hid_unregister_driver(&sapphire_driver);
+       sapphire_destroy();
+}
+
+module_init(sapphire_init);
+module_exit(sapphire_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Lord");
diff --git a/sapphire-remote-6.6/sapphire.h b/sapphire-remote-6.6/sapphire.h
new file mode 100644 (file)
index 0000000..f6305b4
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * sapphire.h
+ *
+ * Copyright Mark Lord <mlord@pobox.com>, 2012-2015.
+ * http://rtr.ca/sapphire_remote/
+ *
+ * Button definitions shared with sapphire_keymap.sh
+ * and the external glue driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; specifically version 2 of the License (GPLv2).
+ */
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+
+/*
+ * These enum's are shared with external keymaps and the sapphire_keymap.sh script:
+ */
+enum { /*
+        * BEGIN_PARSING (marker used by script that extracts these for sapphire_keymap.sh)
+        *
+        * Raw button values from the Sapphire remote:
+        *
+        */
+       SAPPHIRE_UP             = 0x00520000 , /* UP             */
+       SAPPHIRE_DOWN           = 0x00510000 , /* DOWN           */
+       SAPPHIRE_RIGHT          = 0x004f0000 , /* RIGHT          */
+       SAPPHIRE_LEFT           = 0x00500000 , /* LEFT           */
+       SAPPHIRE_ENTEROK        = 0x00280000 , /* ENTER/OK       */
+       SAPPHIRE_BACK           = 0x00040003 , /* BACK           */
+       SAPPHIRE_PLAY           = 0x40000003 , /* PLAY           */
+       SAPPHIRE_PAUSE          = 0x80000003 , /* PAUSE          */
+       SAPPHIRE_VOLUP          = 0x00002003 , /* VOL+           */
+       SAPPHIRE_VOLDOWN        = 0x00004003 , /* VOL-           */
+       SAPPHIRE_CHUP           = 0x00010003 , /* CH/PG+         */
+       SAPPHIRE_CHDOWN         = 0x00020003 , /* CH/PG-         */
+       SAPPHIRE_MUTE           = 0x00001003 , /* MUTE           */
+       SAPPHIRE_RECORD         = 0x00008003 , /* RECORD         */
+       SAPPHIRE_FWD            = 0x08000003 , /* FWD            */
+       SAPPHIRE_REW            = 0x04000003 , /* REW            */
+       SAPPHIRE_ANGLE          = 0x00010004 , /* ANGLE          */
+       SAPPHIRE_SAP            = 0x00100004 , /* SAP            */
+       SAPPHIRE_DVDMENU        = 0x00040004 , /* DVDMENU        */
+       SAPPHIRE_INFOEPG        = 0x02000003 , /* INFO/EPG       */
+       SAPPHIRE_TAB            = 0x00000103 , /* TAB            */
+       SAPPHIRE_BACKTAB        = 0x00000203 , /* BACKTAB        */
+       SAPPHIRE_RADIO          = 0x00000403 , /* RADIO          */
+       SAPPHIRE_LASTCH         = 0x00400004 , /* LASTCH         */
+       SAPPHIRE_LANGUAGE       = 0x00020004 , /* LANGUAGE       */
+       SAPPHIRE_TELETEXTCC     = 0x00200004 , /* TELETEXT/CC    */
+       SAPPHIRE_SUBTITLE       = 0x00080004 , /* SUBTITLE       */
+       SAPPHIRE_HOMEHOUSE      = 0x00000104 , /* HOME (house)   */
+       SAPPHIRE_BLUEVIDEOS     = 0x00002004 , /* BLUE/VIDEOS    */
+       SAPPHIRE_LIVETV         = 0x00000204 , /* LIVETV         */
+       SAPPHIRE_REDDVDVCD      = 0x00008004 , /* RED/DVD/VCD    */
+       SAPPHIRE_YELLOWPICTURES = 0x00001004 , /* YELLOW/PICTURES*/
+       SAPPHIRE_1              = 0x001e0000 , /* 1              */
+       SAPPHIRE_2              = 0x001f0000 , /* 2              */
+       SAPPHIRE_3              = 0x00200000 , /* 3              */
+       SAPPHIRE_4              = 0x00210000 , /* 4              */
+       SAPPHIRE_5              = 0x00220000 , /* 5              */
+       SAPPHIRE_6              = 0x00230000 , /* 6              */
+       SAPPHIRE_7              = 0x00240000 , /* 7              */
+       SAPPHIRE_8              = 0x00250000 , /* 8              */
+       SAPPHIRE_9              = 0x00260000 , /* 9              */
+       SAPPHIRE_0              = 0x00270000 , /* 0              */
+       SAPPHIRE_STOP           = 0x00100003 , /* STOP           */
+       SAPPHIRE_POWER          = 0x00000202 , /* POWER          */
+       SAPPHIRE_CLEAR          = 0x004c0000 , /* CLEAR          */
+       SAPPHIRE_GREENMUSIC     = 0x00000804 , /* GREEN/MUSIC    */
+
+       /*
+        * A second set of "virtual" sapphire buttons,
+        * not for normal mapping use.  Instead, these are
+        * intended for use with the external "glue" module.
+        * If you don't know what that is, then just ignore these!
+        */
+       XAPPHIRE_UP             = 0x005200f0 , /* UP             */
+       XAPPHIRE_DOWN           = 0x005100f0 , /* DOWN           */
+       XAPPHIRE_RIGHT          = 0x004f00f0 , /* RIGHT          */
+       XAPPHIRE_LEFT           = 0x005000f0 , /* LEFT           */
+       XAPPHIRE_ENTEROK        = 0x002800f0 , /* ENTER/OK       */
+       XAPPHIRE_BACK           = 0x000400f3 , /* BACK           */
+       XAPPHIRE_PLAY           = 0x400000f3 , /* PLAY           */
+       XAPPHIRE_PAUSE          = 0x800000f3 , /* PAUSE          */
+       XAPPHIRE_VOLUP          = 0x000020f3 , /* VOL+           */
+       XAPPHIRE_VOLDOWN        = 0x000040f3 , /* VOL-           */
+       XAPPHIRE_CHUP           = 0x000100f3 , /* CH/PG+         */
+       XAPPHIRE_CHDOWN         = 0x000200f3 , /* CH/PG-         */
+       XAPPHIRE_MUTE           = 0x000010f3 , /* MUTE           */
+       XAPPHIRE_RECORD         = 0x000080f3 , /* RECORD         */
+       XAPPHIRE_FWD            = 0x080000f3 , /* FWD            */
+       XAPPHIRE_REW            = 0x040000f3 , /* REW            */
+       XAPPHIRE_ANGLE          = 0x000100f4 , /* ANGLE          */
+       XAPPHIRE_SAP            = 0x001000f4 , /* SAP            */
+       XAPPHIRE_DVDMENU        = 0x000400f4 , /* DVDMENU        */
+       XAPPHIRE_INFOEPG        = 0x020000f3 , /* INFO/EPG       */
+       XAPPHIRE_TAB            = 0x000001f3 , /* TAB            */
+       XAPPHIRE_BACKTAB        = 0x000002f3 , /* BACKTAB        */
+       XAPPHIRE_RADIO          = 0x000004f3 , /* RADIO          */
+       XAPPHIRE_LASTCH         = 0x004000f4 , /* LASTCH         */
+       XAPPHIRE_LANGUAGE       = 0x000200f4 , /* LANGUAGE       */
+       XAPPHIRE_TELETEXTCC     = 0x002000f4 , /* TELETEXT/CC    */
+       XAPPHIRE_SUBTITLE       = 0x000800f4 , /* SUBTITLE       */
+       XAPPHIRE_HOMEHOUSE      = 0x000001f4 , /* HOME (house)   */
+       XAPPHIRE_BLUEVIDEOS     = 0x000020f4 , /* BLUE/VIDEOS    */
+       XAPPHIRE_LIVETV         = 0x000002f4 , /* LIVETV         */
+       XAPPHIRE_REDDVDVCD      = 0x000080f4 , /* RED/DVD/VCD    */
+       XAPPHIRE_YELLOWPICTURES = 0x000010f4 , /* YELLOW/PICTURES*/
+       XAPPHIRE_1              = 0x001e00f0 , /* 1              */
+       XAPPHIRE_2              = 0x001f00f0 , /* 2              */
+       XAPPHIRE_3              = 0x002000f0 , /* 3              */
+       XAPPHIRE_4              = 0x002100f0 , /* 4              */
+       XAPPHIRE_5              = 0x002200f0 , /* 5              */
+       XAPPHIRE_6              = 0x002300f0 , /* 6              */
+       XAPPHIRE_7              = 0x002400f0 , /* 7              */
+       XAPPHIRE_8              = 0x002500f0 , /* 8              */
+       XAPPHIRE_9              = 0x002600f0 , /* 9              */
+       XAPPHIRE_0              = 0x002700f0 , /* 0              */
+       XAPPHIRE_STOP           = 0x001000f3 , /* STOP           */
+       XAPPHIRE_POWER          = 0x000002f2 , /* POWER          */
+       XAPPHIRE_CLEAR          = 0x004c00f0 , /* CLEAR          */
+       XAPPHIRE_GREENMUSIC     = 0x000008f4 , /* GREEN/MUSIC    */
+
+       /*
+        * Modifier buttons: "OR" these with "regular" KEY_ values as desired:
+        */
+       CTRL                    = 0x80000000 ,
+       SHIFT                   = 0x40000000 ,
+       ALT                     = 0x20000000 ,
+       META                    = 0x10000000 ,
+
+       /*
+        * Special "macro" keys:
+        */
+       KEY_DELAY               = 0x00fffff7 ,
+       KEY_MACRO_0             = 0x00fffff8 ,
+       KEY_MACRO_1             = 0x00fffff9 ,
+       KEY_MACRO_2             = 0x00fffffa ,
+       KEY_MACRO_3             = 0x00fffffb ,
+       KEY_MACRO_4             = 0x00fffffc ,
+       KEY_MACRO_5             = 0x00fffffd ,
+       KEY_MACRO_6             = 0x00fffffe ,
+       KEY_MACRO_7             = 0x00ffffff ,
+
+       /*
+        * Per-button automatic repeat rates:
+        */
+       NO_REPEAT               = 0x00000000 ,  /* no autorepeat */
+       SLOW_REPEAT             = 0x00000004 ,  /* repeats 4 times per second */
+       FAST_REPEAT             = 0x00000008 ,  /* repeats 8 times per second */
+       RAMP_REPEAT             = 0x00000003 ,  /* repeats increase in rate as button is held, up to 10/sec */
+       RAWKEY                  = 0xffffffff ,  /* do not send "release" event until button is really released */
+       LONGKEY                 = 0x08000000 ,  /* no autorepeat; short/long presses send different codes */
+};
+
+/*
+ * This is a hook for external modules to tie into,
+ * allowing other remote-drivers to use the sapphire
+ * driver's translations and advanced features.
+ */
+extern void sapphire_relay (u32 data);
diff --git a/sapphire-remote-6.6/sapphire_keymap.sh.part1 b/sapphire-remote-6.6/sapphire_keymap.sh.part1
new file mode 100644 (file)
index 0000000..e594104
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# sapphire_keymap.sh   by Mark Lord <mlord@pobox.com>, April/2013.
+# http://rtr.ca/sapphire_remote/
+#
+# Script to translate a symbolic keymap file for the Sapphire driver
+# into hex format and feed the results to /proc/sapphire.
+#
+# This can be invoked on the fly, as often as desired.
+# Eg. to change keymaps to match a specific program, such as mythfrontend or xbmc.
+# 
+# By default, this script reads key mappings from /etc/sapphire.keymap
+# but this can be overridden by supplying an alternative path/file as a parameter.
+#
+# Eg.      sapphire_keymap.sh ~/.sapphire_keymap
+#
+
+## Arrays for Buttons, Keycodes, and Modifiers:
+##'
+declare -A K B M
diff --git a/sapphire-remote-6.6/sapphire_keymap.sh.part3 b/sapphire-remote-6.6/sapphire_keymap.sh.part3
new file mode 100644 (file)
index 0000000..d694b3a
--- /dev/null
@@ -0,0 +1,97 @@
+
+PROCFILE=/proc/sapphire
+MYNAME="${0##*/}"
+
+function get_multipart_keycode(){
+       local parts="$1" val=0 zero_ok=0 p v
+       while [ "$parts" != "" ]; do
+               p="${parts%%|*}"
+               [ "$p" = "$parts" ] && parts="" || parts="${parts#*|}"
+               v="${K[$p]}"
+               if [ "$v" = "" ]; then
+                       v="${M[$p]}"
+                       if [ "$v" = "" ]; then
+                               logger -s "$MYNAME: ERROR0: $p: unrecognized key/modifier"
+                               exit 1
+                       fi 
+               fi
+               [ $((v)) -eq 0 ] && zero_ok=1 || val=$((val + v))
+       done
+       [ $val -ne 0 -o $zero_ok -eq 1 ] && printf "0x%08x" $val
+}
+
+function parse_keymap(){
+       local line blabel bval klabel kval olabel oval out i
+       while read line ; do
+               line="${line%%#*}"
+               set -- $line
+               [ "$1" = "" ] && continue
+               if [ "$1" = "REPEAT_RATE" -a "$3" != "" ]; then
+                       out="$1 $2 $3"
+               else
+                       blabel="$1"
+                       bval="${B[$blabel]}"
+                       if [ "$bval" = "" ]; then
+                               logger -s "$MYNAME: ERROR1: $line blabel=$blabel"
+                               exit 1
+                       fi
+                       out=
+                       if [ "${blabel:0:10}" = "KEY_MACRO_" ]; then
+                               out="$bval"
+                               olabel=""
+                               for i in {2..8} ; do
+                                       klabel="${!i}"
+                                       [ "$klabel" = "" ] && break
+                                       olabel="$olabel $klabel"
+                                       kval=$(get_multipart_keycode "$klabel")
+                                       if [ "$kval" = "" ]; then
+                                               logger -s "$MYNAME: ERROR2: $line"
+                                               exit 1
+                                       fi
+                                       out="$out $kval"
+                               done
+                               klabel=""
+                       else
+                               klabel="$2"
+                               olabel="$3"
+                               if [ "$blabel" = "" -o "$klabel" = "" -o "$olabel" = "" ]; then
+                                       logger -s "$MYNAME: ERROR3: $line"
+                                       exit 1
+                               fi
+                               kval=$(get_multipart_keycode "$klabel")
+                               oval=$(get_multipart_keycode "$olabel")
+                               if [ "$bval" = "" -o "$kval" = "" -o "$oval" = "" ]; then
+                                       logger -s "$MYNAME: ERROR4: $blabel=$bval $klabel=$kval $olabel=$oval"
+                                       exit 1
+                               fi
+                               out="$bval $kval $oval"
+                       fi
+               fi
+               [ $VERBOSE -gt 0 ] && echo "echo \"$out\" >$PROCFILE  # $blabel $klabel $olabel" >&2
+               echo "$out" >$PROCFILE || exit 2
+               out=
+       done
+}
+
+if [ ! -e $PROCFILE ]; then
+       echo "$PROCFILE: not found (is driver loaded?)"
+       exit 1
+fi
+
+VERBOSE=0
+while [ "$1" = "-v" -o "$1" = "--verbose" ]; do
+       VERBOSE=$((VERBOSE + 1))
+       shift
+done
+
+KEYMAP="$1"
+[ "$KEYMAP" = "" ] && KEYMAP=/etc/sapphire.keymap
+if [ ! -e "$KEYMAP" ]; then
+       logger -s "$MYNAME: $KEYMAP: not readable"
+       exit 1
+fi
+
+type -all logger &>/dev/null && logcmd="logger -s --" || logcmd=echo
+$logcmd "${0##*/}: sending \"$KEYMAP\" to $PROCFILE"
+cat "$KEYMAP" | parse_keymap
+
diff --git a/sapphire-remote-6.6/sapphire_startup.sh b/sapphire-remote-6.6/sapphire_startup.sh
new file mode 100755 (executable)
index 0000000..96097fb
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Some distros seem to be building the hid-topseed driver into
+# the core kernel image, rather than leaving as a loadable module.
+#
+# So, for our sapphire driver to bind to a device,
+# we first have to get the hid-topseed driver to unbind from it.
+#
+TOPSEED=/sys/bus/hid/drivers/topseed
+SAPPHIRE=/sys/bus/hid/drivers/sapphire
+rmmod hid-topseed      &>/dev/null
+modprobe sapphire      &>/dev/null
+if [ -d $SAPPHIRE -a -e $TOPSEED/unbind ]; then
+       cd $TOPSEED
+       for dev in [0-9]*[-0-9A-F] ; do
+               if [ -e "$dev" ]; then
+                       echo "$dev" > unbind
+                       echo "$dev" > $SAPPHIRE/bind
+               fi
+       done
+fi
+#
+# Ubuntu/Mint kernels (and likely others too) don't like it
+# when we unload and reload the sapphire driver.
+# They disable the USB IR receiver and fail to reenable it.
+# The workaround below seems to restore functionality.
+#
+cd $SAPPHIRE || exit 1
+dev="$(/bin/ls -1d [0-9]*[0-9A-F] 2>/dev/null | head -1)"
+if [ "$dev" != "" -a -e "$dev" ]; then
+       if cd -P "$dev/../.." ; then
+               if [ -e authorized ]; then
+                       echo 0 > authorized
+                       echo 1 > authorized
+               fi
+               cd - >/dev/null
+       fi
+fi
+[ -e /etc/sapphire.keymap -a -x /usr/local/bin/sapphire_keymap.sh ] && /usr/local/bin/sapphire_keymap.sh
+exit 0