From d8c3c47b86cda823892c560a938ed01f17511985 Mon Sep 17 00:00:00 2001 From: TangoCash Date: Tue, 12 Sep 2017 16:10:27 +0200 Subject: [PATCH] adding armbox --- Makefile.am | 6 + acinclude.m4 | 23 +- configure.ac | 7 + include/audio_hal.h | 3 + include/cs_api.h | 2 + include/dmx_hal.h | 2 + include/playback_hal.h | 2 + include/pwrmngr.h | 2 + include/record_hal.h | 2 + include/video_hal.h | 2 + libarmbox/Makefile.am | 28 + libarmbox/README.libtriple | 72 + libarmbox/audio.cpp | 480 ++++ libarmbox/audio_lib.h | 109 + libarmbox/audio_mixer.cpp | 68 + libarmbox/audio_mixer.h | 36 + libarmbox/cs_api.h | 66 + libarmbox/dmx.cpp | 615 ++++++ libarmbox/dmx_cs.h | 1 + libarmbox/dmx_lib.h | 72 + libarmbox/hardware_caps.c | 43 + libarmbox/init.cpp | 311 +++ libarmbox/init_cs.h | 2 + libarmbox/init_lib.h | 5 + libarmbox/irmp.c | 4284 ++++++++++++++++++++++++++++++++++++ libarmbox/irmp.h | 528 +++++ libarmbox/irmpconfig.h | 191 ++ libarmbox/lirmp_input.cpp | 445 ++++ libarmbox/lirmp_input.h | 7 + libarmbox/playback_gst.cpp | 837 +++++++ libarmbox/playback_gst.h | 97 + libarmbox/pwrmngr.cpp | 102 + libarmbox/pwrmngr.h | 53 + libarmbox/record.cpp | 383 ++++ libarmbox/record_lib.h | 57 + libarmbox/video.cpp | 1053 +++++++++ libarmbox/video_lib.h | 218 ++ 37 files changed, 10212 insertions(+), 2 deletions(-) create mode 100644 libarmbox/Makefile.am create mode 100644 libarmbox/README.libtriple create mode 100644 libarmbox/audio.cpp create mode 100644 libarmbox/audio_lib.h create mode 100644 libarmbox/audio_mixer.cpp create mode 100644 libarmbox/audio_mixer.h create mode 100644 libarmbox/cs_api.h create mode 100644 libarmbox/dmx.cpp create mode 100644 libarmbox/dmx_cs.h create mode 100644 libarmbox/dmx_lib.h create mode 100644 libarmbox/hardware_caps.c create mode 100644 libarmbox/init.cpp create mode 100644 libarmbox/init_cs.h create mode 100644 libarmbox/init_lib.h create mode 100644 libarmbox/irmp.c create mode 100644 libarmbox/irmp.h create mode 100644 libarmbox/irmpconfig.h create mode 100644 libarmbox/lirmp_input.cpp create mode 100644 libarmbox/lirmp_input.h create mode 100644 libarmbox/playback_gst.cpp create mode 100644 libarmbox/playback_gst.h create mode 100644 libarmbox/pwrmngr.cpp create mode 100644 libarmbox/pwrmngr.h create mode 100644 libarmbox/record.cpp create mode 100644 libarmbox/record_lib.h create mode 100644 libarmbox/video.cpp create mode 100644 libarmbox/video_lib.h diff --git a/Makefile.am b/Makefile.am index b901233..4fd06a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,3 +52,9 @@ libstb_hal_la_LIBADD += \ libeplayer3/libeplayer3.la \ libdvbci/libdvbci.la endif +if BOXTYPE_ARMBOX +libstb_hal_test_LDADD += -lasound +SUBDIRS += libarmbox +libstb_hal_la_LIBADD += \ + libarmbox/libarmbox.la +endif diff --git a/acinclude.m4 b/acinclude.m4 index 6931874..f36f39a 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -273,7 +273,7 @@ _TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,WARN) AC_DEFUN([TUXBOX_BOXTYPE],[ AC_ARG_WITH(boxtype, - [ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,azbox,generic,duckbox,spark7162], + [ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,azbox,generic,duckbox,spark7162,armbox], [case "${withval}" in dbox2|dreambox|ipbox|tripledragon|coolstream|azbox|generic) BOXTYPE="$withval" @@ -330,6 +330,10 @@ AC_ARG_WITH(boxtype, BOXTYPE="duckbox" BOXMODEL="$withval" ;; + armbox) + BOXTYPE="armbox" + BOXMODEL="$withval" + ;; *) AC_MSG_ERROR([bad value $withval for --with-boxtype]) ;; esac], [BOXTYPE="generic"]) @@ -338,7 +342,8 @@ AC_ARG_WITH(boxmodel, [ --with-boxmodel valid for dreambox: dm500, dm500plus, dm600pvr, dm56x0, dm7000, dm7020, dm7025 valid for ipbox: ip200, ip250, ip350, ip400 valid for duckbox: ufs910, ufs912, ufs913, ufs922, atevio7500, fortis_hdbox, octagon1008, hs7110, hs7810a, hs7119, hs7819, dp7000, cuberevo, cuberevo_mini, cuberevo_mini2, cuberevo_250hd, cuberevo_2000hd, cuberevo_3000hd, ipbox9900, ipbox99, ipbox55, arivalink200, tf7700, hl101 - valid for spark: spark, spark7162], + valid for spark: spark, spark7162 + valid for armbox: armbox], [case "${withval}" in dm500|dm500plus|dm600pvr|dm56x0|dm7000|dm7020|dm7025) if test "$BOXTYPE" = "dreambox"; then @@ -375,6 +380,13 @@ AC_ARG_WITH(boxmodel, AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) fi ;; + armbox) + if test "$BOXTYPE" = "armbox"; then + BOXMODEL="$withval" + else + AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) + fi + ;; *) AC_MSG_ERROR([unsupported value $withval for --with-boxmodel]) ;; @@ -395,6 +407,7 @@ AM_CONDITIONAL(BOXTYPE_COOL, test "$BOXTYPE" = "coolstream") AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark") AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic") AM_CONDITIONAL(BOXTYPE_DUCKBOX, test "$BOXTYPE" = "duckbox") +AM_CONDITIONAL(BOXTYPE_ARMBOX, test "$BOXTYPE" = "armbox") AM_CONDITIONAL(BOXMODEL_DM500,test "$BOXMODEL" = "dm500") AM_CONDITIONAL(BOXMODEL_DM500PLUS,test "$BOXMODEL" = "dm500plus") @@ -437,6 +450,8 @@ AM_CONDITIONAL(BOXMODEL_HL101,test "$BOXMODEL" = "hl101") AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi") +AM_CONDITIONAL(BOXMODEL_ARMBOX,test "$BOXMODEL" = "armbox") + if test "$BOXTYPE" = "dbox2"; then AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2]) elif test "$BOXTYPE" = "azbox"; then @@ -451,6 +466,8 @@ elif test "$BOXTYPE" = "coolstream"; then AC_DEFINE(HAVE_COOL_HARDWARE, 1, [building for a coolstream]) elif test "$BOXTYPE" = "spark"; then AC_DEFINE(HAVE_SPARK_HARDWARE, 1, [building for a goldenmedia 990 or edision pingulux]) +elif test "$BOXTYPE" = "armbox"; then + AC_DEFINE(HAVE_ARM_HARDWARE, 1, [building for a armbox]) elif test "$BOXTYPE" = "generic"; then AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC]) elif test "$BOXTYPE" = "duckbox"; then @@ -522,6 +539,8 @@ elif test "$BOXMODEL" = "hl101"; then AC_DEFINE(BOXMODEL_HL101, 1, [hl101]) elif test "$BOXMODEL" = "raspi"; then AC_DEFINE(BOXMODEL_RASPI, 1, [Raspberry pi]) +elif test "$BOXMODEL" = "armbox"; then + AC_DEFINE(BOXMODEL_ARMBOX, 1, [armbox]) fi ]) diff --git a/configure.ac b/configure.ac index 82df74a..6335ce9 100644 --- a/configure.ac +++ b/configure.ac @@ -47,6 +47,12 @@ if test "$enable_gstreamer_10" = "yes"; then PKG_CHECK_MODULES([GSTREAMER_VIDEO], [gstreamer-video-1.0]) fi +if test x$BOXTYPE = xarmbox ; then + PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0]) + PKG_CHECK_MODULES([GSTREAMER_AUDIO], [gstreamer-audio-1.0]) + PKG_CHECK_MODULES([GSTREAMER_VIDEO], [gstreamer-video-1.0]) +fi + if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1]) PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) @@ -66,6 +72,7 @@ libduckbox/Makefile libdvbci/Makefile libtriple/Makefile libspark/Makefile +libarmbox/Makefile raspi/Makefile tools/Makefile ]) diff --git a/include/audio_hal.h b/include/audio_hal.h index 984f2ef..6f50cd5 100644 --- a/include/audio_hal.h +++ b/include/audio_hal.h @@ -7,6 +7,9 @@ #elif HAVE_SPARK_HARDWARE #include "../libspark/audio_lib.h" #include "../libspark/audio_mixer.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/audio_lib.h" +#include "../libarmbox/audio_mixer.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/audio_lib.h" #elif HAVE_GENERIC_HARDWARE diff --git a/include/cs_api.h b/include/cs_api.h index 7364bff..58913c2 100644 --- a/include/cs_api.h +++ b/include/cs_api.h @@ -5,6 +5,8 @@ #include "../libduckbox/cs_api.h" #elif HAVE_SPARK_HARDWARE #include "../libspark/cs_api.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/cs_api.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/cs_api.h" #elif HAVE_GENERIC_HARDWARE diff --git a/include/dmx_hal.h b/include/dmx_hal.h index db4330f..db8ca72 100644 --- a/include/dmx_hal.h +++ b/include/dmx_hal.h @@ -7,6 +7,8 @@ #include "../libspark/dmx_lib.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/dmx_lib.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/dmx_lib.h" #elif HAVE_GENERIC_HARDWARE #if BOXMODEL_RASPI #include "../raspi/dmx_lib.h" diff --git a/include/playback_hal.h b/include/playback_hal.h index 82ec96a..982a856 100644 --- a/include/playback_hal.h +++ b/include/playback_hal.h @@ -5,6 +5,8 @@ #include "../libduckbox/playback_libeplayer3.h" #elif HAVE_SPARK_HARDWARE #include "../libspark/playback_libeplayer3.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/playback_gst.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/playback.h" #elif HAVE_GENERIC_HARDWARE diff --git a/include/pwrmngr.h b/include/pwrmngr.h index dea558f..74fc806 100644 --- a/include/pwrmngr.h +++ b/include/pwrmngr.h @@ -5,6 +5,8 @@ #include "../libduckbox/pwrmngr.h" #elif HAVE_SPARK_HARDWARE #include "../libspark/pwrmngr.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/pwrmngr.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/pwrmngr.h" #elif HAVE_GENERIC_HARDWARE diff --git a/include/record_hal.h b/include/record_hal.h index e4467b5..40f50e6 100644 --- a/include/record_hal.h +++ b/include/record_hal.h @@ -5,6 +5,8 @@ #include "../libduckbox/record_lib.h" #elif HAVE_SPARK_HARDWARE #include "../libspark/record_lib.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/record_lib.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/record_lib.h" #elif HAVE_GENERIC_HARDWARE diff --git a/include/video_hal.h b/include/video_hal.h index ec63331..db01265 100644 --- a/include/video_hal.h +++ b/include/video_hal.h @@ -5,6 +5,8 @@ #include "../libduckbox/video_lib.h" #elif HAVE_SPARK_HARDWARE #include "../libspark/video_lib.h" +#elif HAVE_ARM_HARDWARE +#include "../libarmbox/video_lib.h" #elif HAVE_AZBOX_HARDWARE #include "../azbox/video_lib.h" #elif HAVE_GENERIC_HARDWARE diff --git a/libarmbox/Makefile.am b/libarmbox/Makefile.am new file mode 100644 index 0000000..e3218f7 --- /dev/null +++ b/libarmbox/Makefile.am @@ -0,0 +1,28 @@ +noinst_LTLIBRARIES = libarmbox.la + +AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS +AM_CPPFLAGS += \ + -I$(top_srcdir)/common \ + -I$(top_srcdir)/include + +AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing + +AM_LDFLAGS = \ + -lOpenThreads \ + @AVFORMAT_LIBS@ \ + @AVUTIL_LIBS@ \ + @AVCODEC_LIBS@ \ + @SWRESAMPLE_LIBS@ \ + -lpthread -lasound -lass -lrt \ + -lgstreamer-1.0 + +libarmbox_la_SOURCES = \ + hardware_caps.c \ + dmx.cpp \ + video.cpp \ + audio.cpp \ + audio_mixer.cpp \ + init.cpp \ + playback_gst.cpp \ + pwrmngr.cpp \ + record.cpp diff --git a/libarmbox/README.libtriple b/libarmbox/README.libtriple new file mode 100644 index 0000000..89f65b7 --- /dev/null +++ b/libarmbox/README.libtriple @@ -0,0 +1,72 @@ +libtriple reimplements the interfaces of the libcoolstrem library for +the Tripledragon receiver. + +There are a few debugging or configuration helpers which affect the +way libtriple does some things. They are all configured by exporting +environment variables, which are described here: + +TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on + SCART pin 8, probably not useful for anyone but me + +TRIPLE_DEBUG=... - controls various debugging levels in libtriple + valid values for the different component: + audio 0x01 + video 0x02 + demux 0x04 + play 0x08 + power 0x10 + init 0x20 + ca 0x40 + record 0x80 + all 0xff + multiple levels are added / ORed together, so if you want to + debug record and playback code, do "export TRIPLE_DEBUG=0x88" + for audio & video use TRIPLE_DEBUG=0x3 + +DSP_DEVICE +MIX_DEVICE - alternative audio devices for the audioplayer and internet + radio. Those are used to output music to e.g. USB audio devices. + Here is what you need to do: + * look in /dev/sound which devices are already there. Probably + /dev/sound/dsp and /dev/sound/mixer, created by the tdoss driver + * make sure that the USB HC driver is loaded: + modprobe ohci-hcd + * load the USB audio driver: + modprobe audio + * plug in your USB audio device, check with "dmesg" that it is + recognised by the kernel + * look in /dev/sound which new devices are there. Probably it's + /dev/sound/dsp1 and /dev/sound/mixer1. If there are more - well + it's time to experiment ;) + * export DSP_DEVICE=/dev/sound/dsp1 and MIX_DEVICE=/dev/sound/mixer1 + (of course the devices you found in the last step) + * from the same shell you exported the variables, start neutrino + (make sure that an already running neutrino is stopped before you + do that) + * start the audioplayer, play a track. Look for log lines like + [LT:106b5788:audio ] PrepareClipPlay: dsp_dev /dev/sound/dsp1 mix_dev /dev/sound/mixer1 + * if it works - fine :-) + * if it does not work, look for: + PrepareClipPlay: DSP_DEVICE is set (/dev/sound/dsp1) but cannot be opened, fall back to /dev/sound/dsp + PrepareClipPlay: dsp_dev /dev/sound/dsp mix_dev /dev/sound/mixer1 + PrepareClipPlay: open mixer /dev/sound/mixer1 failed (No such file or directory) + * this basically means that the device is not there. Different errors + will get different messages - I cannot trigger those now, so you'll + need to find them out by yourself ;) + * another possible messag you may get is: + PrepareClipPlay: more than one mixer control: devmask 00000021 stereo 00000021 + This means that your device has more than one mixer. The set bit + numbers in the devmask are the different mixers, in this case + it would be number 0 and 5. To select one of those, export + MIX_NUMBER=5 or MIX_NUMBER=0 (this code is untested, there may + be bugs) + + So now I found out what devices to use, but how do I make that permanent? + That's easy: + * create or extend /etc/rcS.local with + modprobe ohci-hcd + modprobe audio + * create or extend /etc/profile.local with + export DSP_DEVICE=/dev/sound/dsp1 + export MIX_DEVICE=/dev/sound/mixer1 + * reboot. Enjoy. diff --git a/libarmbox/audio.cpp b/libarmbox/audio.cpp new file mode 100644 index 0000000..7a55814 --- /dev/null +++ b/libarmbox/audio.cpp @@ -0,0 +1,480 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "audio_lib.h" +#include "audio_mixer.h" +#include "lt_debug.h" + +#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args) + +#include + +cAudio * audioDecoder = NULL; + +static int proc_put(const char *path, const char *value, const int len) +{ + int ret, ret2; + int pfd = open(path, O_WRONLY); + if (pfd < 0) + return pfd; + ret = write(pfd, value, len); + ret2 = close(pfd); + if (ret2 < 0) + return ret2; + return ret; +} + +cAudio::cAudio(void *, void *, void *) +{ + fd = -1; + clipfd = -1; + mixer_fd = -1; + + mixerAnalog = mixerHDMI = mixerSPDIF = NULL; + volumeAnalog = volumeHDMI = volumeSPDIF = 0; + mixersMuted = false; + + openDevice(); + Muted = false; +} + +cAudio::~cAudio(void) +{ + closeMixers(); + closeDevice(); +} + +void cAudio::openDevice(void) +{ + openMixers(); + + if (fd < 0) + { + if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) + lt_info("openDevice: open failed (%m)\n"); + fcntl(fd, F_SETFD, FD_CLOEXEC); + do_mute(true, false); + } + else + lt_info("openDevice: already open (fd = %d)\n", fd); +} + +void cAudio::closeDevice(void) +{ + closeMixers(); + + if (fd > -1) { + close(fd); + fd = -1; + } + if (clipfd > -1) { + close(clipfd); + clipfd = -1; + } + if (mixer_fd > -1) { + close(mixer_fd); + mixer_fd = -1; + } +} + +int cAudio::do_mute(bool enable, bool remember) +{ + lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember); + char str[4]; + + if (remember) + Muted = enable; + + sprintf(str, "%d", Muted); + proc_put("/proc/stb/audio/j1_mute", str, strlen(str)); + + if (!enable) + { + int f = open("/proc/stb/avs/0/volume", O_RDWR); + read(f, str, 4); + close(f); + str[3] = '\0'; + proc_put("/proc/stb/avs/0/volume", str, strlen(str)); + } + return 0; +} + +int map_volume(const int volume) +{ + unsigned char vol = volume; + if (vol > 100) + vol = 100; + + vol = 63 - vol * 63 / 100; + return vol; +} + + +int cAudio::setVolume(unsigned int left, unsigned int right) +{ + lt_debug("%s(%d, %d)\n", __func__, left, right); + + volume = (left + right) / 2; + int v = map_volume(volume); +#if 0 + if (clipfd != -1 && mixer_fd != -1) { + int tmp = 0; + /* not sure if left / right is correct here, but it is always the same anyways ;-) */ + if (! Muted) + tmp = left << 8 | right; + int ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp); + if (ret == -1) + lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); + return ret; + } +#endif + + char str[4]; + sprintf(str, "%d", v); + + proc_put("/proc/stb/avs/0/volume", str, strlen(str)); + return 0; +} + +int cAudio::Start(void) +{ + int ret; + ret = ioctl(fd, AUDIO_PLAY); + return ret; +} + +int cAudio::Stop(void) +{ + return ioctl(fd, AUDIO_STOP); +} + +bool cAudio::Pause(bool Pcm) +{ + ioctl(fd, Pcm ? AUDIO_PAUSE : AUDIO_CONTINUE, 1); + return true; +} + +void cAudio::SetSyncMode(AVSYNC_TYPE Mode) +{ + lt_debug("%s %d\n", __func__, Mode); + ioctl(fd, AUDIO_SET_AV_SYNC, Mode); +} + +// E2 streamtype values. These correspond to +// player2/linux/drivers/media/dvb/stm/dvb/dvb_audio.c:AudioIoctlSetBypassMode +#define AUDIO_STREAMTYPE_AC3 0 +#define AUDIO_STREAMTYPE_MPEG 1 +#define AUDIO_STREAMTYPE_DTS 2 +#define AUDIO_STREAMTYPE_AAC 8 +#define AUDIO_STREAMTYPE_AACHE 9 + +void cAudio::SetStreamType(AUDIO_FORMAT type) +{ + int bypass = AUDIO_STREAMTYPE_MPEG; + lt_debug("%s %d\n", __FUNCTION__, type); + StreamType = type; + + switch (type) + { + case AUDIO_FMT_DD_PLUS: + case AUDIO_FMT_DOLBY_DIGITAL: + bypass = AUDIO_STREAMTYPE_AC3; + break; + case AUDIO_FMT_AAC: + bypass = AUDIO_STREAMTYPE_AAC; + break; + case AUDIO_FMT_AAC_PLUS: + bypass = AUDIO_STREAMTYPE_AACHE; + break; + case AUDIO_FMT_DTS: + bypass = AUDIO_STREAMTYPE_DTS; + break; + default: + break; + } + + // Normaly the encoding should be set using AUDIO_SET_ENCODING + // But as we implemented the behavior to bypass (cause of e2) this is correct here + if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0) + lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__); +} + +int cAudio::setChannel(int channel) +{ + lt_debug("%s %d\n", __FUNCTION__, channel); + return 0; +} + +int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) +{ + int fmt; + unsigned int devmask, stereo, usable; + const char *dsp_dev = getenv("DSP_DEVICE"); + const char *mix_dev = getenv("MIX_DEVICE"); + lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); + if (clipfd > -1) { + lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); + return -1; + } + mixer_num = -1; + /* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE + * if this device cannot be opened, we fall back to the internal OSS device + * Example: + * modprobe snd-usb-audio + * export DSP_DEVICE=/dev/sound/dsp2 + * export MIX_DEVICE=/dev/sound/mixer2 + * neutrino + */ + if ((!dsp_dev) || (access(dsp_dev, W_OK))) { + if (dsp_dev) + lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened," + " fall back to /dev/dsp1\n", __func__, dsp_dev); + dsp_dev = "/dev/dsp1"; + } + lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */ + /* the tdoss dsp driver seems to work only on the second open(). really. */ + clipfd = open(dsp_dev, O_WRONLY); + if (clipfd < 0) { + lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__); + return -1; + } + fcntl(clipfd, F_SETFD, FD_CLOEXEC); + /* no idea if we ever get little_endian == 0 */ + if (little_endian) + fmt = AFMT_S16_BE; + else + fmt = AFMT_S16_LE; + if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt)) + perror("SNDCTL_DSP_SETFMT"); + if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch)) + perror("SNDCTL_DSP_CHANNELS"); + if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate)) + perror("SNDCTL_DSP_SPEED"); + if (ioctl(clipfd, SNDCTL_DSP_RESET)) + perror("SNDCTL_DSP_RESET"); + + if (!mix_dev) + return 0; + + mixer_fd = open(mix_dev, O_RDWR); + if (mixer_fd < 0) { + lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev); + /* not a real error */ + return 0; + } + if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { + lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__); + devmask = 0; + } + if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__); + stereo = 0; + } + usable = devmask & stereo; + if (usable == 0) { + lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n", + __func__, devmask, stereo); + close(mixer_fd); + mixer_fd = -1; + return 0; /* TODO: should we treat this as error? */ + } + /* __builtin_popcount needs GCC, it counts the set bits... */ + if (__builtin_popcount (usable) != 1) { + /* TODO: this code is not yet tested as I have only single-mixer devices... */ + lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n" + "%s: querying MIX_NUMBER environment variable...\n", + __func__, devmask, stereo, __func__); + const char *tmp = getenv("MIX_NUMBER"); + if (tmp) + mixer_num = atoi(tmp); + lt_info("%s: mixer_num is %d -> device %08x\n", + __func__, mixer_num, (mixer_num >= 0) ? (1 << mixer_num) : 0); + /* no error checking, you'd better know what you are doing... */ + } else { + mixer_num = 0; + while (!(usable & 0x01)) { + mixer_num++; + usable >>= 1; + } + } + setVolume(volume, volume); + + return 0; +} + +int cAudio::WriteClip(unsigned char *buffer, int size) +{ + int ret; + // lt_debug("cAudio::%s\n", __FUNCTION__); + if (clipfd < 0) { + lt_info("%s: clipfd not yet opened\n", __FUNCTION__); + return -1; + } + ret = write(clipfd, buffer, size); + if (ret < 0) + lt_info("%s: write error (%m)\n", __FUNCTION__); + return ret; +}; + +int cAudio::StopClip() +{ + lt_debug("%s\n", __FUNCTION__); + if (clipfd < 0) { + lt_info("%s: clipfd not yet opened\n", __FUNCTION__); + return -1; + } + close(clipfd); + clipfd = -1; + if (mixer_fd >= -1) { + close(mixer_fd); + mixer_fd = -1; + } + setVolume(volume, volume); + return 0; +}; + +void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) +{ + lt_debug("%s\n", __FUNCTION__); + type = 0; + layer = 0; + freq = 0; + bitrate = 0; + mode = 0; +#if 0 + unsigned int atype; + static const int freq_mpg[] = {44100, 48000, 32000, 0}; + static const int freq_ac3[] = {48000, 44100, 32000, 0}; + scratchl2 i; + if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0) + perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP"); + if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0) + perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS"); + + type = atype; +#if 0 +/* this does not work, some of the values are negative?? */ + AMPEGStatus A; + memcpy(&A, &i.word00, sizeof(i.word00)); + layer = A.audio_mpeg_layer; + mode = A.audio_mpeg_mode; + bitrate = A.audio_mpeg_bitrate; + switch(A.audio_mpeg_frequency) +#endif + /* layer and bitrate are not used anyway... */ + layer = 0; //(i.word00 >> 17) & 3; + bitrate = 0; //(i.word00 >> 12) & 3; + switch (type) + { + case 0: /* MPEG */ + mode = (i.word00 >> 6) & 3; + freq = freq_mpg[(i.word00 >> 10) & 3]; + break; + case 1: /* AC3 */ + mode = (i.word00 >> 28) & 7; + freq = freq_ac3[(i.word00 >> 16) & 3]; + break; + default: + mode = 0; + freq = 0; + } + //fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode); +#endif +}; + +void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) +{ + lt_debug("%s\n", __FUNCTION__); +}; + +void cAudio::SetHdmiDD(bool enable) +{ + const char *opt[] = { "pcm", "spdif" }; + lt_debug("%s %d\n", __func__, enable); + proc_put("/proc/stb/hdmi/audio_source", opt[enable], strlen(opt[enable])); +} + +void cAudio::SetSpdifDD(bool enable) +{ + lt_debug("%s %d\n", __func__, enable); + setBypassMode(!enable); +} + +void cAudio::ScheduleMute(bool On) +{ + lt_debug("%s %d\n", __FUNCTION__, On); +} + +void cAudio::EnableAnalogOut(bool enable) +{ + lt_debug("%s %d\n", __FUNCTION__, enable); +} + +#define AUDIO_BYPASS_ON 0 +#define AUDIO_BYPASS_OFF 1 +void cAudio::setBypassMode(bool disable) +{ + const char *opt[] = { "passthrough", "downmix" }; + lt_debug("%s %d\n", __func__, disable); + proc_put("/proc/stb/audio/ac3", opt[disable], strlen(opt[disable])); +} + +void cAudio::openMixers(void) +{ + if (!mixerAnalog) + mixerAnalog = new mixerVolume("Analog", "1"); + if (!mixerHDMI) + mixerHDMI = new mixerVolume("HDMI", "1"); + if (!mixerSPDIF) + mixerSPDIF = new mixerVolume("SPDIF", "1"); +} + +void cAudio::closeMixers(void) +{ + delete mixerAnalog; + delete mixerHDMI; + delete mixerSPDIF; + mixerAnalog = mixerHDMI = mixerSPDIF = NULL; +} + +void cAudio::setMixerVolume(const char *name, long value, bool remember) +{ + if (!strcmp(name, "Analog")) { + mixerAnalog->setVolume(value); + if (remember) + volumeAnalog = value; + } + if (!strcmp(name, "HDMI")) { + mixerHDMI->setVolume(value); + if (remember) + volumeHDMI = value; + } + if (!strcmp(name, "SPDIF")) { + mixerSPDIF->setVolume(value); + if (remember) + volumeSPDIF = value; + } +} + +void cAudio::muteMixers(bool m) +{ + if (m && !mixersMuted) { + mixersMuted = true; + setMixerVolume("Analog", 0, false); + setMixerVolume("HDMI", 0, false); + setMixerVolume("SPDIF", 0, false); + } else if (!m && mixersMuted) { + mixersMuted = false; + setMixerVolume("Analog", volumeAnalog, false); + setMixerVolume("HDMI", volumeHDMI, false); + setMixerVolume("SPDIF", volumeSPDIF, false); + } +} diff --git a/libarmbox/audio_lib.h b/libarmbox/audio_lib.h new file mode 100644 index 0000000..b2b78d6 --- /dev/null +++ b/libarmbox/audio_lib.h @@ -0,0 +1,109 @@ +/* public header file */ + +#ifndef _AUDIO_TD_H_ +#define _AUDIO_TD_H_ +#include "../common/cs_types.h" + +typedef enum +{ + AUDIO_SYNC_WITH_PTS, + AUDIO_NO_SYNC, + AUDIO_SYNC_AUDIO_MASTER +} AUDIO_SYNC_MODE; + +typedef enum { + HDMI_ENCODED_OFF, + HDMI_ENCODED_AUTO, + HDMI_ENCODED_FORCED +} HDMI_ENCODED_MODE; + +typedef enum +{ + AUDIO_FMT_AUTO = 0, + AUDIO_FMT_MPEG, + AUDIO_FMT_MP3, + AUDIO_FMT_DOLBY_DIGITAL, + AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL, + AUDIO_FMT_AAC, + AUDIO_FMT_AAC_PLUS, + AUDIO_FMT_DD_PLUS, + AUDIO_FMT_DTS, + AUDIO_FMT_AVS, + AUDIO_FMT_MLP, + AUDIO_FMT_WMA, + AUDIO_FMT_MPG1, // TD only. For Movieplayer / cPlayback + AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP +} AUDIO_FORMAT; + +class mixerVolume; + +class cAudio +{ + friend class cPlayback; + private: + int fd; + bool Muted; + + int clipfd; /* for pcm playback */ + int mixer_fd; /* if we are using the OSS mixer */ + int mixer_num; /* oss mixer to use, if any */ + + AUDIO_FORMAT StreamType; + AUDIO_SYNC_MODE SyncMode; + bool started; + + int volume; + + void openDevice(void); + void closeDevice(void); + + int do_mute(bool enable, bool remember); + void setBypassMode(bool disable); + + mixerVolume *mixerAnalog, *mixerHDMI, *mixerSPDIF; + int volumeAnalog, volumeHDMI, volumeSPDIF; + bool mixersMuted; + + public: + /* construct & destruct */ + cAudio(void *, void *, void *); + ~cAudio(void); + + void *GetHandle() { return NULL; }; + /* shut up */ + int mute(bool remember = true) { return do_mute(true, remember); }; + int unmute(bool remember = true) { return do_mute(false, remember); }; + + /* volume, min = 0, max = 255 */ + int setVolume(unsigned int left, unsigned int right); + int getVolume(void) { return volume;} + bool getMuteStatus(void) { return Muted; }; + + /* start and stop audio */ + int Start(void); + int Stop(void); + bool Pause(bool Pcm = true); + void SetStreamType(AUDIO_FORMAT type); + AUDIO_FORMAT GetStreamType(void) { return StreamType; } + void SetSyncMode(AVSYNC_TYPE Mode); + + /* select channels */ + int setChannel(int channel); + int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian); + int WriteClip(unsigned char * buffer, int size); + int StopClip(); + void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode); + void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level); + bool IsHdmiDDSupported() { return true; }; + void SetHdmiDD(bool enable); + void SetSpdifDD(bool enable); + void ScheduleMute(bool On); + void EnableAnalogOut(bool enable); + + void openMixers(void); + void closeMixers(void); + void setMixerVolume(const char *name, long value, bool remember = true); + void muteMixers(bool m = true); +}; + +#endif diff --git a/libarmbox/audio_mixer.cpp b/libarmbox/audio_mixer.cpp new file mode 100644 index 0000000..f361209 --- /dev/null +++ b/libarmbox/audio_mixer.cpp @@ -0,0 +1,68 @@ +/* + * audio_mixer.cpp + * + * (C) 2012 martii + * + * 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; 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, see . + */ + +#include + +mixerVolume::mixerVolume(const char *name, const char *card, long volume) { + snd_mixer_selem_id_t *sid = NULL; + elem = NULL; + handle = NULL; + min = 0; + max = 100; + char cardId[10]; + + if (!name || !card) + return; + + int cx = snd_card_get_index(card); + if (cx < 0 || cx > 31) + return; + snprintf(cardId, sizeof(cardId), "hw:%i", cx); + + if (0 > snd_mixer_open(&handle, 0)) + return; + if (0 > snd_mixer_attach(handle, cardId)) + return; + if (0 > snd_mixer_selem_register(handle, NULL, NULL)) + return; + if (0 > snd_mixer_load(handle)) + return; + snd_mixer_selem_id_alloca(&sid); + if (!sid) + return; + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, name); + elem = snd_mixer_find_selem(handle, sid); + if (elem) { + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + setVolume(volume); + } +} +mixerVolume::~mixerVolume() +{ + if (handle) + snd_mixer_close(handle); +} + +bool mixerVolume::setVolume(long volume) { + return elem + && (volume > -1) + && (volume < 101) + && !snd_mixer_selem_set_playback_volume_all(elem, min + volume * (max - min)/100); +} diff --git a/libarmbox/audio_mixer.h b/libarmbox/audio_mixer.h new file mode 100644 index 0000000..2a6f6fc --- /dev/null +++ b/libarmbox/audio_mixer.h @@ -0,0 +1,36 @@ +/* + * audio_mixer.h + * + * (C) 2012 martii + * + * 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; 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, see . + */ + +#ifndef __AUDIO_MIXER_H__ +#define __AUDIO_MIXER_H__ +#include + +class mixerVolume +{ + private: + long min, max; + snd_mixer_t *handle; + snd_mixer_elem_t* elem; + public: + mixerVolume(const char *selem_name, const char *Card, long volume = -1); + ~mixerVolume(void); + bool setVolume(long volume); +}; +#endif + diff --git a/libarmbox/cs_api.h b/libarmbox/cs_api.h new file mode 100644 index 0000000..fb5d613 --- /dev/null +++ b/libarmbox/cs_api.h @@ -0,0 +1,66 @@ +/* compatibility header for tripledragon. I'm lazy, so I just left it + as "cs_api.h" so that I don't need too many ifdefs in the code */ + +#ifndef __CS_API_H_ +#define __CS_API_H_ + +#include "init_lib.h" +typedef void (*cs_messenger) (unsigned int msg, unsigned int data); + +#if 0 +enum CS_LOG_MODULE { + CS_LOG_CI = 0, + CS_LOG_HDMI_CEC, + CS_LOG_HDMI, + CS_LOG_VIDEO, + CS_LOG_VIDEO_DRM, + CS_LOG_AUDIO, + CS_LOG_DEMUX, + CS_LOG_DENC, + CS_LOG_PVR_RECORD, + CS_LOG_PVR_PLAY, + CS_LOG_POWER_CTRL, + CS_LOG_POWER_CLK, + CS_LOG_MEM, + CS_LOG_API, +}; +#endif + +inline void cs_api_init() +{ + init_td_api(); +}; + +inline void cs_api_exit() +{ + shutdown_td_api(); +}; + +#define cs_malloc_uncached malloc +#define cs_free_uncached free + +// Callback function helpers +static inline void cs_register_messenger(cs_messenger) { return; }; +static inline void cs_deregister_messenger(void) { return; }; +//cs_messenger cs_get_messenger(void); + +#if 0 +// Logging functions +void cs_log_enable(void); +void cs_log_disable(void); +void cs_log_message(const char *prefix, const char *fmt, ...); +void cs_log_module_enable(enum CS_LOG_MODULE module); +void cs_log_module_disable(enum CS_LOG_MODULE module); +void cs_log_module_message(enum CS_LOG_MODULE module, const char *fmt, ...); + +// TS Routing +unsigned int cs_get_ts_output(void); +int cs_set_ts_output(unsigned int port); + +// Serial nr and revision accessors +unsigned long long cs_get_serial(void); +#endif +/* compat... HD1 seems to be version 6. everything newer ist > 6... */ +static inline unsigned int cs_get_revision(void) { return 1; }; +extern int cnxt_debug; +#endif //__CS_API_H_ diff --git a/libarmbox/dmx.cpp b/libarmbox/dmx.cpp new file mode 100644 index 0000000..d3ba4e7 --- /dev/null +++ b/libarmbox/dmx.cpp @@ -0,0 +1,615 @@ +/* + * cDemux implementation for SH4 receivers (tested on fulan spark and + * fulan spark7162 hardware) + * + * derived from libtriple/dmx_td.cpp + * + * (C) 2010-2013 Stefan Seyfried + * + * 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; 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, see . + */ + +/* + * Theory of operation (or "why is this dmx_source thing so strange and + * what is the _open() good for?") + * + * the sh4 pti driver, driving the /dev/dvb/adapter0/dmxN devices, can + * apparently only map one input to on demux device at a time, so e.g. + * DMX_SOURCE_FRONT1 -> demux0 + * DMX_SOURCE_FRONT2 -> demux0 + * DMX_SOURCE_FRONT1 -> demux1 + * does not work. The driver makes sure that a one-to-one mapping of + * DMX_SOURCE_FRONTn to demuxM is maintained, and it does by e.g changing + * the default of + * FRONT0 -> demux0 + * FRONT1 -> demux1 + * FRONT2 -> demux2 + * to + * FRONT1 -> demux0 + * FRONT0 -> demux1 + * FRONT2 -> demux2 + * if you do a DMX_SET_SOURCE(FRONT1) ioctl on demux0. + * This means, it also changes demux1's source on the SET_SOURCE ioctl on + * demux0, potentially disturbing any operation on demux1 (e.g. recording). + * + * In order to avoid this, I do not change the source->demuxdev mapping + * but instead just always use the demux device that is attached to the + * correct source. + * + * The tricky part is, that the source might actually be changed after + * Open() has been called, so Open() gets a dummy placeholder that just + * sets some variables while the real device open is put into _open(). + * _open() gets called later, whenever the device is actually used or + * configured and -- if the source has changed -- closes the old and + * opens the correct new device node. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "dmx_lib.h" +#include "lt_debug.h" + +/* Ugh... see comment in destructor for details... */ +#include "video_lib.h" +extern cVideo *videoDecoder; + +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, this, args) +#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_DEMUX, NULL, args) + +#define dmx_err(_errfmt, _errstr, _revents) do { \ + uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\ + if (dmx_type == DMX_PSI_CHANNEL) { \ + _pid = s_flt.pid; _f = s_flt.filter.filter[0]; \ + } else { \ + _pid = p_flt.pid; \ + }; \ + lt_info("%s " _errfmt " fd:%d, ev:0x%x %s pid:0x%04hx flt:0x%02hx\n", \ + __func__, _errstr, fd, _revents, DMX_T[dmx_type], _pid, _f); \ +} while(0); + +cDemux *videoDemux = NULL; +cDemux *audioDemux = NULL; +//cDemux *pcrDemux = NULL; + +static const char *DMX_T[] = { + "DMX_INVALID", + "DMX_VIDEO", + "DMX_AUDIO", + "DMX_PES", + "DMX_PSI", + "DMX_PIP", + "DMX_TP", + "DMX_PCR" +}; + +/* this is the number of different cDemux() units, not the number of + * /dev/dvb/.../demuxX devices! */ +#define NUM_DEMUX 4 +/* the current source of each cDemux unit */ +static int dmx_source[NUM_DEMUX] = { 0, 0, 0, 0 }; + +/* map the device numbers. */ +#define NUM_DEMUXDEV 3 +static const char *devname[NUM_DEMUXDEV] = { + "/dev/dvb/adapter0/demux0", + "/dev/dvb/adapter0/demux1", + "/dev/dvb/adapter0/demux2" +}; +/* did we already DMX_SET_SOURCE on that demux device? */ +static bool init[NUM_DEMUXDEV] = { false, false, false }; + +/* uuuugly */ +static int dmx_tp_count = 0; +#define MAX_TS_COUNT 1 + +cDemux::cDemux(int n) +{ + if (n < 0 || n >= NUM_DEMUX) + { + lt_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n); + num = 0; + } + else + num = n; + fd = -1; + measure = false; + last_measure = 0; + last_data = 0; + last_source = -1; +} + +cDemux::~cDemux() +{ + lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd); + Close(); + /* in zapit.cpp, videoDemux is deleted after videoDecoder + * in the video watchdog, we access videoDecoder + * the thread still runs after videoDecoder has been deleted + * => set videoDecoder to NULL here to make the check in the + * watchdog thread pick this up. + * This is ugly, but it saves me from changing neutrino + * + * if the delete order in neutrino will ever be changed, this + * will blow up badly :-( + */ + if (dmx_type == DMX_VIDEO_CHANNEL) + videoDecoder = NULL; +} + +bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) +{ + if (fd > -1) + lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); + + dmx_type = pes_type; + buffersize = uBufferSize; + + /* return code is unchecked anyway... */ + return true; +} + +bool cDemux::_open(void) +{ + int flags = O_RDWR|O_CLOEXEC; + int devnum = dmx_source[num]; + if (last_source == devnum) { + lt_debug("%s #%d: source (%d) did not change\n", __func__, num, last_source); + if (fd > -1) + return true; + } + if (fd > -1) { + /* we changed source -> close and reopen the fd */ + lt_debug("%s #%d: FD ALREADY OPENED fd = %d lastsource %d devnum %d\n", + __func__, num, fd, last_source, devnum); + close(fd); + } + + if (dmx_type != DMX_PSI_CHANNEL) + flags |= O_NONBLOCK; + + fd = open(devname[devnum], flags); + if (fd < 0) + { + lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]); + return false; + } + lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__, + num, DMX_T[dmx_type], dmx_type, buffersize, fd); + + /* this would actually need locking, but the worst that weill happen is, that + * we'll DMX_SET_SOURCE twice per device, so don't bother... */ + if (!init[devnum]) + { + /* this should not change anything... */ + int n = DMX_SOURCE_FRONT0 + devnum; + lt_info("%s: setting %s to source %d\n", __func__, devname[devnum], n); + if (ioctl(fd, DMX_SET_SOURCE, &n) < 0) + lt_info("%s DMX_SET_SOURCE failed!\n", __func__); + else + init[devnum] = true; + } + if (buffersize == 0) + buffersize = 0xffff; // may or may not be reasonable --martii + if (buffersize > 0) + { + /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ + if (ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize) < 0) + lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__); + } + + last_source = devnum; + return true; +} + +void cDemux::Close(void) +{ + lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd); + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return; + } + + pesfds.clear(); + ioctl(fd, DMX_STOP); + close(fd); + fd = -1; + if (measure) + return; + if (dmx_type == DMX_TP_CHANNEL) + { + dmx_tp_count--; + if (dmx_tp_count < 0) + { + lt_info("%s dmx_tp_count < 0!!\n", __func__); + dmx_tp_count = 0; + } + } +} + +bool cDemux::Start(bool) +{ + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + ioctl(fd, DMX_START); + return true; +} + +bool cDemux::Stop(void) +{ + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + ioctl(fd, DMX_STOP); + return true; +} + +int cDemux::Read(unsigned char *buff, int len, int timeout) +{ +#if 0 + if (len != 4095 && timeout != 10) + fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n", + __FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout); +#endif + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __func__, num); + return -1; + } + int rc; + int to = timeout; + struct pollfd ufds; + ufds.fd = fd; + ufds.events = POLLIN|POLLPRI|POLLERR; + ufds.revents = 0; + + /* hack: if the frontend loses and regains lock, the demuxer often will not + * return from read(), so as a "emergency exit" for e.g. NIT scan, set a (long) + * timeout here */ + if (dmx_type == DMX_PSI_CHANNEL && timeout <= 0) + to = 60 * 1000; + + if (to > 0) + { + retry: + rc = ::poll(&ufds, 1, to); + if (!rc) + { + if (timeout == 0) /* we took the emergency exit */ + { + dmx_err("timed out for timeout=0!, %s", "", 0); + return -1; /* this timeout is an error */ + } + return 0; // timeout + } + else if (rc < 0) + { + dmx_err("poll: %s,", strerror(errno), 0) + //lt_info("%s poll: %m\n", __FUNCTION__); + /* happens, when running under gdb... */ + if (errno == EINTR) + goto retry; + return -1; + } +#if 0 + if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */ + { + dmx_err("received %s,", "POLLERR", ufds.revents); + /* this seems to happen sometimes at recording start, without bad effects */ + return 0; + } +#endif + if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */ + { + dmx_err("received %s,", "POLLHUP", ufds.revents); + return -1; + } + if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */ + { + dmx_err("received %s, please report!", "POLLIN", ufds.revents); + return 0; + } + } + + rc = ::read(fd, buff, len); + //fprintf(stderr, "fd %d ret: %d\n", fd, rc); + if (rc < 0) + dmx_err("read: %s", strerror(errno), 0); + + return rc; +} + +bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filter, + const unsigned char * const mask, int len, int timeout, + const unsigned char * const negmask) +{ + memset(&s_flt, 0, sizeof(s_flt)); + + _open(); + + if (len > DMX_FILTER_SIZE) + { + lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE); + len = DMX_FILTER_SIZE; + } + s_flt.pid = pid; + s_flt.timeout = timeout; + memcpy(s_flt.filter.filter, filter, len); + memcpy(s_flt.filter.mask, mask, len); + if (negmask != NULL) + memcpy(s_flt.filter.mode, negmask, len); + + s_flt.flags = DMX_IMMEDIATE_START|DMX_CHECK_CRC; + + int to = 0; + switch (filter[0]) { + case 0x00: /* program_association_section */ + to = 2000; + break; + case 0x01: /* conditional_access_section */ + to = 6000; + break; + case 0x02: /* program_map_section */ + to = 1500; + break; + case 0x03: /* transport_stream_description_section */ + to = 10000; + break; + /* 0x04 - 0x3F: reserved */ + case 0x40: /* network_information_section - actual_network */ + to = 10000; + break; + case 0x41: /* network_information_section - other_network */ + to = 15000; + break; + case 0x42: /* service_description_section - actual_transport_stream */ + to = 10000; + break; + /* 0x43 - 0x45: reserved for future use */ + case 0x46: /* service_description_section - other_transport_stream */ + to = 10000; + break; + /* 0x47 - 0x49: reserved for future use */ + case 0x4A: /* bouquet_association_section */ + to = 11000; + break; + /* 0x4B - 0x4D: reserved for future use */ + case 0x4E: /* event_information_section - actual_transport_stream, present/following */ + to = 2000; + break; + case 0x4F: /* event_information_section - other_transport_stream, present/following */ + to = 10000; + break; + /* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */ + /* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */ + case 0x70: /* time_date_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + s_flt.flags |= DMX_ONESHOT; + //s_flt.pid = 0x0014; + to = 30000; + break; + case 0x71: /* running_status_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + to = 0; + break; + case 0x72: /* stuffing_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + to = 0; + break; + case 0x73: /* time_offset_section */ + s_flt.flags |= DMX_ONESHOT; + //s_flt.pid = 0x0014; + to = 30000; + break; + /* 0x74 - 0x7D: reserved for future use */ + case 0x7E: /* discontinuity_information_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + to = 0; + break; + case 0x7F: /* selection_information_section */ + to = 0; + break; + /* 0x80 - 0x8F: ca_message_section */ + /* 0x90 - 0xFE: user defined */ + /* 0xFF: reserved */ + default: + break; +// return -1; + } + /* the negmask == NULL is a hack: the users of negmask are PMT-update + * and sectionsd EIT-Version change. And they really want no timeout + * if timeout == 0 instead of "default timeout" */ + if (timeout == 0 && negmask == NULL) + s_flt.timeout = to; + + lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d to:%d flags:%x flt[0]:%02x\n", __func__, num, + pid, fd, DMX_T[dmx_type], len, s_flt.timeout,s_flt.flags, s_flt.filter.filter[0]); +#if 0 + fprintf(stderr,"filt: ");for(int i=0;i= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) + return false; + + lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); + + _open(); + + memset(&p_flt, 0, sizeof(p_flt)); + p_flt.pid = pid; + p_flt.output = DMX_OUT_DECODER; + p_flt.input = DMX_IN_FRONTEND; + p_flt.flags = DMX_IMMEDIATE_START; + + switch (dmx_type) { + case DMX_PCR_ONLY_CHANNEL: + p_flt.pes_type = DMX_PES_PCR; + break; + case DMX_AUDIO_CHANNEL: + p_flt.pes_type = DMX_PES_AUDIO; + break; + case DMX_VIDEO_CHANNEL: + p_flt.pes_type = DMX_PES_VIDEO; + break; + case DMX_PIP_CHANNEL: /* PIP is a special version of DMX_VIDEO_CHANNEL */ + p_flt.pes_type = DMX_PES_VIDEO1; + break; + case DMX_PES_CHANNEL: + p_flt.pes_type = DMX_PES_OTHER; + p_flt.output = DMX_OUT_TAP; + break; + case DMX_TP_CHANNEL: + p_flt.pes_type = DMX_PES_OTHER; + p_flt.output = DMX_OUT_TSDEMUX_TAP; + break; + default: + lt_info("%s #%d invalid dmx_type %d!\n", __func__, num, dmx_type); + return false; + } + return (ioctl(fd, DMX_SET_PES_FILTER, &p_flt) >= 0); +} + +void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) +{ + lt_debug("%s #%d\n", __FUNCTION__, num); +} + +void *cDemux::getBuffer() +{ + lt_debug("%s #%d\n", __FUNCTION__, num); + return NULL; +} + +void *cDemux::getChannel() +{ + lt_debug("%s #%d\n", __FUNCTION__, num); + return NULL; +} + +bool cDemux::addPid(unsigned short Pid) +{ + lt_debug("%s: pid 0x%04hx\n", __func__, Pid); + pes_pids pfd; + int ret; + if (dmx_type != DMX_TP_CHANNEL) + { + lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + return false; + } + _open(); + if (fd == -1) + lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); + pfd.fd = fd; /* dummy */ + pfd.pid = Pid; + pesfds.push_back(pfd); + ret = (ioctl(fd, DMX_ADD_PID, &Pid)); + if (ret < 0) + lt_info("%s: DMX_ADD_PID (%m) pid=%hx\n", __func__, Pid); + return (ret != -1); +} + +void cDemux::removePid(unsigned short Pid) +{ + if (dmx_type != DMX_TP_CHANNEL) + { + lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + return; + } + for (std::vector::iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + if ((*i).pid == Pid) { + lt_debug("removePid: removing demux fd %d pid 0x%04x\n", fd, Pid); + if (ioctl(fd, DMX_REMOVE_PID, Pid) < 0) + lt_info("%s: (DMX_REMOVE_PID, 0x%04hx): %m\n", __func__, Pid); + pesfds.erase(i); + return; /* TODO: what if the same PID is there multiple times */ + } + } + lt_info("%s pid 0x%04x not found\n", __FUNCTION__, Pid); +} + +void cDemux::getSTC(int64_t * STC) +{ + /* apparently I can only get the PTS of the video decoder, + * but that's good enough for dvbsub */ + lt_debug("%s #%d\n", __func__, num); + int64_t pts = 0; + if (videoDecoder) + pts = videoDecoder->GetPTS(); + *STC = pts; +} + +int cDemux::getUnit(void) +{ + lt_debug("%s #%d\n", __FUNCTION__, num); + /* just guessed that this is the right thing to do. + right now this is only used by the CA code which is stubbed out + anyway */ + return num; +} + +bool cDemux::SetSource(int unit, int source) +{ + if (unit >= NUM_DEMUX || unit < 0) { + lt_info_c("%s: unit (%d) out of range, NUM_DEMUX %d\n", __func__, unit, NUM_DEMUX); + return false; + } + lt_info_c("%s(%d, %d) => %d to %d\n", __func__, unit, source, dmx_source[unit], source); + if (source < 0 || source >= NUM_DEMUXDEV) + lt_info_c("%s(%d, %d) ERROR: source %d out of range!\n", __func__, unit, source, source); + else + dmx_source[unit] = source; + return true; +} + +int cDemux::GetSource(int unit) +{ + if (unit >= NUM_DEMUX || unit < 0) { + lt_info_c("%s: unit (%d) out of range, NUM_DEMUX %d\n", __func__, unit, NUM_DEMUX); + return -1; + } + lt_info_c("%s(%d) => %d\n", __func__, unit, dmx_source[unit]); + return dmx_source[unit]; +} diff --git a/libarmbox/dmx_cs.h b/libarmbox/dmx_cs.h new file mode 100644 index 0000000..175d8cb --- /dev/null +++ b/libarmbox/dmx_cs.h @@ -0,0 +1 @@ +#include "dmx_lib.h" diff --git a/libarmbox/dmx_lib.h b/libarmbox/dmx_lib.h new file mode 100644 index 0000000..af87a02 --- /dev/null +++ b/libarmbox/dmx_lib.h @@ -0,0 +1,72 @@ +#ifndef __DEMUX_TD_H +#define __DEMUX_TD_H + +#include +#include +#include +#include +#include +#include "../common/cs_types.h" + +#define MAX_DMX_UNITS 4 + +typedef enum +{ + DMX_INVALID = 0, + DMX_VIDEO_CHANNEL = 1, + DMX_AUDIO_CHANNEL, + DMX_PES_CHANNEL, + DMX_PSI_CHANNEL, + DMX_PIP_CHANNEL, + DMX_TP_CHANNEL, + DMX_PCR_ONLY_CHANNEL +} DMX_CHANNEL_TYPE; + +typedef struct +{ + int fd; + unsigned short pid; +} pes_pids; + +class cDemux +{ + private: + int num; + int fd; + int buffersize; + bool measure; + uint64_t last_measure, last_data; + DMX_CHANNEL_TYPE dmx_type; + std::vector pesfds; + struct dmx_sct_filter_params s_flt; + struct dmx_pes_filter_params p_flt; + int last_source; + bool _open(void); + public: + + bool Open(DMX_CHANNEL_TYPE pes_type, void * unused = NULL, int bufsize = 0); + void Close(void); + bool Start(bool record = false); + bool Stop(void); + int Read(unsigned char *buff, int len, int Timeout = 0); + bool sectionFilter(unsigned short pid, const unsigned char * const filter, const unsigned char * const mask, int len, int Timeout = 0, const unsigned char * const negmask = NULL); + bool pesFilter(const unsigned short pid); + void SetSyncMode(AVSYNC_TYPE mode); + void * getBuffer(); + void * getChannel(); + DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; }; + bool addPid(unsigned short pid); + void getSTC(int64_t * STC); + int getUnit(void); + static bool SetSource(int unit, int source); + static int GetSource(int unit); + // TD only functions + int getFD(void) { return fd; }; /* needed by cPlayback class */ + void removePid(unsigned short Pid); /* needed by cRecord class */ + std::vector getPesPids(void) { return pesfds; }; + // + cDemux(int num = 0); + ~cDemux(); +}; + +#endif //__DEMUX_H diff --git a/libarmbox/hardware_caps.c b/libarmbox/hardware_caps.c new file mode 100644 index 0000000..5dd7fd8 --- /dev/null +++ b/libarmbox/hardware_caps.c @@ -0,0 +1,43 @@ +/* + * determine the capabilities of the hardware. + * part of libstb-hal + * + * (C) 2010-2012 Stefan Seyfried + * + * License: GPL v2 or later + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FP_DEV "/dev/vfd" +static int initialized = 0; +static hw_caps_t caps; + +hw_caps_t *get_hwcaps(void) +{ + if (initialized) + return ∩︀ + + memset(&caps, 0, sizeof(hw_caps_t)); + + initialized = 1; + caps.has_CI = 0; + caps.can_cec = 1; + caps.can_shutdown = 1; + caps.display_type = HW_DISPLAY_LED_NUM; + caps.can_set_display_brightness = 0; + caps.has_HDMI = 1; + caps.display_xres = 4; + strcpy(caps.boxvendor, "armbox"); + strcpy(caps.boxname, "armbox"); + strcpy(caps.boxarch,caps.boxname); + return ∩︀ +} diff --git a/libarmbox/init.cpp b/libarmbox/init.cpp new file mode 100644 index 0000000..b52ecca --- /dev/null +++ b/libarmbox/init.cpp @@ -0,0 +1,311 @@ + +#include + +#include + +#include "init_lib.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pwrmngr.h" + +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VIRTUALINPUT "/sys/devices/virtual/input" +#define DEVINPUT "/dev/input" + +typedef struct { + const char *name; + const char *desc; + int fd; + unsigned int major; + unsigned int minor; + time_t next_discovery; +} input_device_t; + +static input_device_t input_device[] = { + { "/dev/input/nevis_ir", "lircd", -1, 0, 0, 0 }, + { "/dev/input/tdt_rc", "TDT RC event driver", -1, 0, 0, 0 }, + { "/dev/input/fulan_fp", "fulan front panel buttons", -1, 0, 0, 0 }, + { "/dev/input/event0", NULL, -1, 0, 0, 0 }, + { "/dev/input/event1", NULL, -1, 0, 0, 0 }, + { "/dev/input/event2", NULL, -1, 0, 0, 0 }, + { "/dev/input/event3", NULL, -1, 0, 0, 0 }, + { "/dev/input/event4", NULL, -1, 0, 0, 0 }, + { "/dev/input/event5", NULL, -1, 0, 0, 0 }, + { "/dev/input/event6", NULL, -1, 0, 0, 0 }, + { "/dev/input/event7", NULL, -1, 0, 0, 0 }, + { NULL, NULL, -1, 0, 0, 0 } +}; + +#define number_of_input_devices (sizeof(input_device)/sizeof(input_device_t) - 1) + +static void do_mknod(int i, char *d_name) { + char name[255]; + int dev = -1; + // I've no idea how the event device number is actually calculated. Just loop. --martii + + for (int j = 0; j < 99 && dev < 0; j++) { + snprintf(name, sizeof(name), VIRTUALINPUT "/%s/event%d/dev", d_name, j); + dev = open (name, O_RDONLY); + } + + if (dev > -1) { + char buf[255]; + int l = read(dev, buf, sizeof(buf) - 1); + close(dev); + if (l > -1) { + buf[l] = 0; + if (2 == sscanf(buf, "%d:%d", &input_device[i].major, &input_device[i].minor)) { + mknod(input_device[i].name, 0666 | S_IFCHR, + gnu_dev_makedev(input_device[i].major, input_device[i].minor)); + } + } + } +} + +static void create_input_devices (void) { + DIR *d = opendir (VIRTUALINPUT); + if (d) { + struct dirent *e; + while ((e = readdir(d))) { + char name[255]; + if (e->d_name[0] == '.') + continue; + snprintf(name, sizeof(name), VIRTUALINPUT "/%s/name", e->d_name); + int n = open(name, O_RDONLY); + if (n > -1) { + char buf[255]; + int l = read(n, buf, sizeof(buf) - 1); + close(n); + if (l > 1) { + do + buf[l--] = 0; + while (l > 1 && buf[l] == '\n'); + + for (unsigned int i = 0; i < number_of_input_devices; i++) + if (input_device[i].desc && !strcmp(buf, input_device[i].desc)) { + do_mknod(i, e->d_name); + break; + } + } + } + } + closedir(d); + } + // remove any event* files left that point to our "well-known" inputs + d = opendir (DEVINPUT); + if (d) { + struct dirent *e; + while ((e = readdir(d))) { + char name[255]; + if (strncmp(e->d_name, "event", 5)) + continue; + snprintf(name, sizeof(name), DEVINPUT "/%s", e->d_name); + struct stat st; + if (stat(name, &st)) + continue; + for (unsigned int i = 0; i < number_of_input_devices; i++) + if (input_device[i].major && + gnu_dev_major(st.st_rdev) == input_device[i].major && + gnu_dev_minor(st.st_rdev) == input_device[i].minor) + unlink(name); + } + closedir(d); + } +} + +static pthread_t inmux_task = 0; +static int inmux_thread_running = 0; + +static void open_input_devices(void) { + time_t now = time(NULL); + for (unsigned int i = 0; i < number_of_input_devices; i++) + if ((input_device[i].fd < 0) && (input_device[i].next_discovery <= now)) { + input_device[i].next_discovery = now + 60; + input_device[i].fd = open(input_device[i].name, O_RDWR | O_NONBLOCK); + } +} + +static void reopen_input_devices(void) { + create_input_devices(); + time_t now = time(NULL); + for (unsigned int i = 0; i < number_of_input_devices; i++) { + input_device[i].next_discovery = now + 60; + int fd = open(input_device[i].name, O_RDWR | O_NONBLOCK); + if (fd > -1) { + if (input_device[i].fd > -1) { + dup2(fd, input_device[i].fd); + close (fd); + } else { + input_device[i].fd = fd; + } + } else if (input_device[i].fd > -1) { + close (input_device[i].fd); + input_device[i].fd = -1; + } + } +} + +static void close_input_devices(void) { + for (unsigned int i = 0; i < number_of_input_devices; i++) + if (input_device[i].fd > -1) { + close(input_device[i].fd); + input_device[i].fd = -1; + } +} + +static void poll_input_devices(void) { + struct pollfd fds[number_of_input_devices]; + input_device_t *inputs[number_of_input_devices]; + int nfds = 0; + for (unsigned int i = 1; i < number_of_input_devices; i++) + if (input_device[i].fd > -1) { + fds[nfds].fd = input_device[i].fd; + fds[nfds].events = POLLIN | POLLHUP | POLLERR; + fds[nfds].revents = 0; + inputs[nfds] = &input_device[i]; + nfds++; + } + + if (nfds == 0) { + // Only a single input device, which happens to be our master. poll() to avoid looping too fast. + fds[0].fd = input_device[0].fd; + fds[0].events = POLLIN | POLLHUP | POLLERR; + fds[0].revents = 0; + poll(fds, 1, 60000 /* ms */); + return; + } + + int r = poll(fds, nfds, 60000 /* ms */); + if (r < 0) { + if (errno != EAGAIN) { + lt_info("%s: poll(): %m\n", __func__); + inmux_thread_running = 0; + } + return; + } + for (int i = 0; i < nfds && r > 0; i++) { + if (fds[i].revents & POLLIN) { +//fprintf(stderr, "### input from fd %d (%s)\n", fds[i].fd, inputs[i]->name); + struct input_event ev; + while (sizeof(ev) == read(fds[i].fd, &ev, sizeof(ev))) + write(input_device[0].fd, &ev, sizeof(ev)); + r--; + } else if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) { +//fprintf(stderr, "### error on %d (%s)\n", fds[i].fd, inputs[i]->name); + close (fds[i].fd); + inputs[i]->fd = -1; + r--; + } + } +} + +static void *inmux_thread(void *) +{ + char threadname[17]; + strncpy(threadname, __func__, sizeof(threadname)); + threadname[16] = 0; + prctl (PR_SET_NAME, (unsigned long)&threadname); + + inmux_thread_running = 1; + while (inmux_thread_running) { + open_input_devices(); + poll_input_devices(); + } + + return NULL; +} + +void start_inmux_thread(void) +{ + input_device[0].fd = open(input_device[0].name, O_RDWR | O_NONBLOCK); // nevis_ir. This is mandatory. + if (input_device[0].fd < 0){ + lt_info("%s: open(%s): %m\n", __func__, input_device[0].name); + return; + } + if (pthread_create(&inmux_task, 0, inmux_thread, NULL) != 0) + { + lt_info("%s: inmux thread pthread_create: %m\n", __func__); + inmux_thread_running = 0; + return; + } + pthread_detach(inmux_task); +} + +void stop_inmux_thread(void) +{ + inmux_thread_running = 0; +} + +static bool initialized = false; + +void init_td_api() +{ + if (!initialized) + lt_debug_init(); + lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel); + if (!initialized) + { + cCpuFreqManager f; + f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */ + create_input_devices(); + start_inmux_thread(); + +#if 0 + /* this is a strange hack: the drivers seem to only work correctly after + * demux0 has been used once. After that, we can use demux1,2,... */ + struct dmx_pes_filter_params p; + int dmx = open("/dev/dvb/adapter0/demux0", O_RDWR|O_CLOEXEC); + if (dmx < 0) + lt_info("%s: ERROR open /dev/dvb/adapter0/demux0 (%m)\n", __func__); + else + { + memset(&p, 0, sizeof(p)); + p.output = DMX_OUT_DECODER; + p.input = DMX_IN_FRONTEND; + p.flags = DMX_IMMEDIATE_START; + p.pes_type = DMX_PES_VIDEO; + ioctl(dmx, DMX_SET_PES_FILTER, &p); + ioctl(dmx, DMX_STOP); + close(dmx); + } +#endif + } + else + reopen_input_devices(); + initialized = true; + lt_info("%s end\n", __FUNCTION__); +} + +void shutdown_td_api() +{ + lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized); + if (initialized) { + stop_inmux_thread(); + close_input_devices(); + } + initialized = false; +} diff --git a/libarmbox/init_cs.h b/libarmbox/init_cs.h new file mode 100644 index 0000000..7f9e341 --- /dev/null +++ b/libarmbox/init_cs.h @@ -0,0 +1,2 @@ +#warning using init_cs.h from libspark +#include "init_lib.h" diff --git a/libarmbox/init_lib.h b/libarmbox/init_lib.h new file mode 100644 index 0000000..d9a6f09 --- /dev/null +++ b/libarmbox/init_lib.h @@ -0,0 +1,5 @@ +#ifndef __INIT_TD_H +#define __INIT_TD_H +void init_td_api(); +void shutdown_td_api(); +#endif diff --git a/libarmbox/irmp.c b/libarmbox/irmp.c new file mode 100644 index 0000000..68b82cc --- /dev/null +++ b/libarmbox/irmp.c @@ -0,0 +1,4284 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * irmp.c - infrared multi-protocol decoder, supports several remote control protocols + * + * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de + * + * $Id: irmp.c,v 1.115 2012/02/21 08:41:46 fm Exp $ + * + * ATMEGA88 @ 8 MHz + * + * Supported mikrocontrollers: + * + * ATtiny45, ATtiny85 + * ATtiny84 + * ATmega8, ATmega16, ATmega32 + * ATmega162 + * ATmega164, ATmega324, ATmega644, ATmega644P, ATmega1284 + * ATmega88, ATmega88P, ATmega168, ATmega168P, ATmega328P + * + * Typical manufacturers of remote controls: + * + * SIRCS - Sony + * NEC - NEC, Yamaha, Canon, Tevion, Harman/Kardon, Hitachi, JVC, Pioneer, Toshiba, Xoro, Orion, and many other Japanese manufacturers + * SAMSUNG - Samsung + * SAMSUNG32 - Samsung + * MATSUSHITA - Matsushita + * KASEIKYO - Panasonic, Denon & other Japanese manufacturers (members of "Japan's Association for Electric Home Application") + * RECS80 - Philips, Nokia, Thomson, Nordmende, Telefunken, Saba + * RC5 - Philips and other European manufacturers + * DENON - Denon, Sharp + * RC6 - Philips and other European manufacturers + * APPLE - Apple + * NUBERT - Nubert Subwoofer System + * B&O - Bang & Olufsen + * PANASONIC - Panasonic (older, yet not implemented) + * GRUNDIG - Grundig + * NOKIA - Nokia + * SIEMENS - Siemens, e.g. Gigaset M740AV + * FDC - FDC IR keyboard + * RCCAR - IR remote control for RC cars + * JVC - JVC + * THOMSON - Thomson + * NIKON - Nikon cameras + * RUWIDO - T-Home + * KATHREIN - Kathrein + * LEGO - Lego Power Functions RC + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SIRCS + * ----- + * + * frame: 1 start bit + 12-20 data bits + no stop bit + * data: 7 command bits + 5 address bits + 0 to 8 additional bits + * + * start bit: data "0": data "1": stop bit: + * -----------------_________ ------_____ ------------______ + * 2400us 600us 600us 600us 1200us 600 us no stop bit + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * NEC + extended NEC + * ------------------------- + * + * frame: 1 start bit + 32 data bits + 1 stop bit + * data NEC: 8 address bits + 8 inverted address bits + 8 command bits + 8 inverted command bits + * data extended NEC: 16 address bits + 8 command bits + 8 inverted command bits + * + * start bit: data "0": data "1": stop bit: + * -----------------_________ ------______ ------________________ ------______.... + * 9000us 4500us 560us 560us 560us 1690 us 560us + * + * + * Repetition frame: + * + * -----------------_________------______ .... ~100ms Pause, then repeat + * 9000us 2250us 560us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SAMSUNG + * ------- + * + * frame: 1 start bit + 16 data(1) bits + 1 sync bit + additional 20 data(2) bits + 1 stop bit + * data(1): 16 address bits + * data(2): 4 ID bits + 8 command bits + 8 inverted command bits + * + * start bit: data "0": data "1": sync bit: stop bit: + * ----------______________ ------______ ------________________ ------______________ ------______.... + * 4500us 4500us 550us 450us 550us 1450us 550us 4500us 550us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SAMSUNG32 + * ---------- + * + * frame: 1 start bit + 32 data bits + 1 stop bit + * data: 16 address bits + 16 command bits + * + * start bit: data "0": data "1": stop bit: + * ----------______________ ------______ ------________________ ------______.... + * 4500us 4500us 550us 450us 550us 1450us 550us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * MATSUSHITA + * ---------- + * + * frame: 1 start bit + 24 data bits + 1 stop bit + * data: 6 custom bits + 6 command bits + 12 address bits + * + * start bit: data "0": data "1": stop bit: + * ----------_________ ------______ ------________________ ------______.... + * 3488us 3488us 872us 872us 872us 2616us 872us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * KASEIKYO + * -------- + * + * frame: 1 start bit + 48 data bits + 1 stop bit + * data: 16 manufacturer bits + 4 parity bits + 4 genre1 bits + 4 genre2 bits + 10 command bits + 2 id bits + 8 parity bits + * + * start bit: data "0": data "1": stop bit: + * ----------______ ------______ ------________________ ------______.... + * 3380us 1690us 423us 423us 423us 1269us 423us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RECS80 + * ------ + * + * frame: 2 start bits + 10 data bits + 1 stop bit + * data: 1 toggle bit + 3 address bits + 6 command bits + * + * start bit: data "0": data "1": stop bit: + * -----_____________________ -----____________ -----______________ ------_______.... + * 158us 7432us 158us 4902us 158us 7432us 158us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RECS80EXT + * --------- + * + * frame: 2 start bits + 11 data bits + 1 stop bit + * data: 1 toggle bit + 4 address bits + 6 command bits + * + * start bit: data "0": data "1": stop bit: + * -----_____________________ -----____________ -----______________ ------_______.... + * 158us 3637us 158us 4902us 158us 7432us 158us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RC5 + RC5X + * ---------- + * + * RC5 frame: 2 start bits + 12 data bits + no stop bit + * RC5 data: 1 toggle bit + 5 address bits + 6 command bits + * RC5X frame: 1 start bit + 13 data bits + no stop bit + * RC5X data: 1 inverted command bit + 1 toggle bit + 5 address bits + 6 command bits + * + * start bit: data "0": data "1": + * ______----- ------______ ______------ + * 889us 889us 889us 889us 889us 889us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * DENON + * ----- + * + * frame: 0 start bits + 16 data bits + stop bit + 65ms pause + 16 inverted data bits + stop bit + * data: 5 address bits + 10 command bits + * + * Theory: + * + * data "0": data "1": + * ------________________ ------______________ + * 275us 775us 275us 1900us + * + * Practice: + * + * data "0": data "1": + * ------________________ ------______________ + * 310us 745us 310us 1780us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RC6 + * --- + * + * RC6 frame: 1 start bit + 1 bit "1" + 3 mode bits + 1 toggle bit + 16 data bits + 2666 us pause + * RC6 data: 8 address bits + 8 command bits + * + * start bit toggle bit "0": toggle bit "1": data/mode "0": data/mode "1": + * ____________------- _______------- -------_______ _______------- -------_______ + * 2666us 889us 889us 889us 889us 889us 444us 444us 444us 444us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * APPLE + * ----- + * + * frame: 1 start bit + 32 data bits + 1 stop bit + * data: 16 address bits + 11100000 + 8 command bits + * + * start bit: data "0": data "1": stop bit: + * -----------------_________ ------______ ------________________ ------______.... + * 9000us 4500us 560us 560us 560us 1690 us 560us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * NUBERT (subwoofer system) + * ------------------------- + * + * frame: 1 start bit + 10 data bits + 1 stop bit + * data: 0 address bits + 10 command bits ? + * + * start bit: data "0": data "1": stop bit: + * ----------_____ ------______ ------________________ ------______.... + * 1340us 340us 500us 1300us 1340us 340us 500us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * BANG_OLUFSEN + * ------------ + * + * frame: 4 start bits + 16 data bits + 1 trailer bit + 1 stop bit + * data: 0 address bits + 16 command bits + * + * 1st start bit: 2nd start bit: 3rd start bit: 4th start bit: + * -----________ -----________ -----_____________ -----________ + * 210us 3000us 210us 3000us 210us 15000us 210us 3000us + * + * data "0": data "1": data "repeat bit": trailer bit: stop bit: + * -----________ -----_____________ -----___________ -----_____________ -----____... + * 210us 3000us 210us 9000us 210us 6000us 210us 12000us 210us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * GRUNDIG + * ------- + * + * packet: 1 start frame + 19,968ms pause + N info frames + 117,76ms pause + 1 stop frame + * frame: 1 pre bit + 1 start bit + 9 data bits + no stop bit + * pause between info frames: 117,76ms + * + * data of start frame: 9 x 1 + * data of info frame: 9 command bits + * data of stop frame: 9 x 1 + * + * pre bit: start bit data "0": data "1": + * ------____________ ------______ ______------ ------______ + * 528us 2639us 528us 528us 528us 528us 528us 528us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * NOKIA: + * ------ + * + * Timing similar to Grundig, but 16 data bits: + * frame: 1 pre bit + 1 start bit + 8 command bits + 8 address bits + no stop bit + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SIEMENS or RUWIDO: + * ------------------ + * + * SIEMENS frame: 1 start bit + 22 data bits + no stop bit + * SIEMENS data: 13 address bits + 1 repeat bit + 7 data bits + 1 unknown bit + * + * start bit data "0": data "1": + * -------_______ _______------- -------_______ + * 250us 250us 250us 250us 250us 250us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * PANASONIC (older protocol, yet not implemented, see also MATSUSHITA, timing very similar) + * ----------------------------------------------------------------------------------------- + * + * frame: 1 start bit + 22 data bits + 1 stop bit + * 22 data bits = 5 custom bits + 6 data bits + 5 inverted custom bits + 6 inverted data bits + * + * European version: T = 456us + * USA & Canada version: T = 422us + * + * start bit: data "0": data "1": stop bit: + * 8T 8T 2T 2T 2T 6T 2T + * -------------____________ ------_____ ------_____________ ------_______.... + * 3648us 3648us 912us 912us 912us 2736us 912us (Europe) + * 3376us 3376us 844us 844us 844us 2532us 844us (US) + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * 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; either version 2 of the License, or + * (at your option) any later version. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#if defined(__18CXX) +#define PIC_C18 // Microchip C18 Compiler +#endif + +#if defined(__PCM__) || defined(__PCB__) || defined(__PCH__) // CCS PIC Compiler instead of AVR +#define PIC_CCS_COMPILER +#endif + +#ifdef unix // test on linux/unix +#include +#include +#include +#include +#include + +/* for crazy lirc stuff... */ +#include +#include +#include +#include + +#define ANALYZE +#define PROGMEM +#define memcpy_P memcpy + +#else // not unix: + +#ifdef WIN32 +#include +#include +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +#define ANALYZE +#define PROGMEM +#define memcpy_P memcpy + +#else + +#if defined (PIC_CCS_COMPILER) || defined(PIC_C18) + +#include +#define PROGMEM +#define memcpy_P memcpy + +#if defined (PIC_CCS_COMPILER) +typedef unsigned int8 uint8_t; +typedef unsigned int16 uint16_t; +#endif + +#else // AVR: + +#include +#include +#include +#include +#include +#include + +#endif // PIC_CCS_COMPILER or PIC_C18 + +#endif // windows +#endif // unix + +#ifndef IRMP_USE_AS_LIB +#include "irmpconfig.h" +#endif +#include "irmp.h" + +#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 || IRMP_SUPPORT_NOKIA_PROTOCOL == 1 || IRMP_SUPPORT_IR60_PROTOCOL == 1 +#define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL 1 +#else +#define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 || IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 +#define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL 1 +#else +#define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || \ + IRMP_SUPPORT_RC6_PROTOCOL == 1 || \ + IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 || \ + IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 || \ + IRMP_SUPPORT_GRUNDIG2_PROTOCOL == 1 || \ + IRMP_SUPPORT_IR60_PROTOCOL +#define IRMP_SUPPORT_MANCHESTER 1 +#else +#define IRMP_SUPPORT_MANCHESTER 0 +#endif + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 +#define IRMP_SUPPORT_SERIAL 1 +#else +#define IRMP_SUPPORT_SERIAL 0 +#endif + +#define IRMP_KEY_REPETITION_LEN (uint16_t)(F_INTERRUPTS * 150.0e-3 + 0.5) // autodetect key repetition within 150 msec + +#define MIN_TOLERANCE_00 1.0 // -0% +#define MAX_TOLERANCE_00 1.0 // +0% + +#define MIN_TOLERANCE_05 0.95 // -5% +#define MAX_TOLERANCE_05 1.05 // +5% + +#define MIN_TOLERANCE_10 0.9 // -10% +#define MAX_TOLERANCE_10 1.1 // +10% + +#define MIN_TOLERANCE_15 0.85 // -15% +#define MAX_TOLERANCE_15 1.15 // +15% + +#define MIN_TOLERANCE_20 0.8 // -20% +#define MAX_TOLERANCE_20 1.2 // +20% + +#define MIN_TOLERANCE_30 0.7 // -30% +#define MAX_TOLERANCE_30 1.3 // +30% + +#define MIN_TOLERANCE_40 0.6 // -40% +#define MAX_TOLERANCE_40 1.4 // +40% + +#define MIN_TOLERANCE_50 0.5 // -50% +#define MAX_TOLERANCE_50 1.5 // +50% + +#define MIN_TOLERANCE_60 0.4 // -60% +#define MAX_TOLERANCE_60 1.6 // +60% + +#define MIN_TOLERANCE_70 0.3 // -70% +#define MAX_TOLERANCE_70 1.7 // +70% + +#define SIRCS_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIRCS_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#if IRMP_SUPPORT_NETBOX_PROTOCOL // only 5% to avoid conflict with NETBOX: +#define SIRCS_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5)) +#else // only 5% + 1 to avoid conflict with RC6: +#define SIRCS_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) +#endif +#define SIRCS_1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIRCS_0_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_0_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIRCS_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define NEC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +// autodetect nec repetition frame within 50 msec: +// NEC seems to send the first repetition frame after 40ms, further repetition frames after 100 ms +#if 0 +#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * NEC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) +#else +#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * 100.0e-3 * MAX_TOLERANCE_20 + 0.5) +#endif + +#define SAMSUNG_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SAMSUNG_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SAMSUNG_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SAMSUNG_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SAMSUNG_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define SAMSUNG_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define SAMSUNG_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define SAMSUNG_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define SAMSUNG_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define SAMSUNG_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) + +#define MATSUSHITA_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define MATSUSHITA_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define MATSUSHITA_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define MATSUSHITA_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define MATSUSHITA_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define MATSUSHITA_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define MATSUSHITA_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define MATSUSHITA_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define MATSUSHITA_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define MATSUSHITA_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) + +#define KASEIKYO_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KASEIKYO_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KASEIKYO_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KASEIKYO_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KASEIKYO_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MIN_TOLERANCE_50 + 0.5) - 1) +#define KASEIKYO_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1) +#define KASEIKYO_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define KASEIKYO_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define KASEIKYO_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MIN_TOLERANCE_50 + 0.5) - 1) +#define KASEIKYO_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1) + +#define RECS80_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MIN_TOLERANCE_00 + 0.5) - 1) +#define RECS80_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RC5_START_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC5_START_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RC5_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC5_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define DENON_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define DENON_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define DENON_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define DENON_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 +#define DENON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5)) // no -1, avoid conflict with RUWIDO +#else +#define DENON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) // be more tolerant +#endif +#define DENON_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define THOMSON_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define THOMSON_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define THOMSON_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define THOMSON_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define THOMSON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define THOMSON_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RC6_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RC6_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RC6_TOGGLE_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_TOGGLE_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RC6_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_60 + 0.5) + 1) // pulses: 300 - 800 +#define RC6_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) // pauses: 300 - 600 + +#define RECS80EXT_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MIN_TOLERANCE_00 + 0.5) - 1) +#define RECS80EXT_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MAX_TOLERANCE_00 + 0.5) + 1) +#define RECS80EXT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) +#define RECS80EXT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) +#define RECS80EXT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80EXT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80EXT_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80EXT_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80EXT_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80EXT_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define NUBERT_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_0_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_0_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) + +#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX ((PAUSE_LEN)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) // value must be below IRMP_TIMEOUT +#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_R_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_R_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define IR60_TIMEOUT_LEN ((uint8_t)(F_INTERRUPTS * IR60_TIMEOUT_TIME * 0.5)) +#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define GRUNDIG_NOKIA_IR60_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define GRUNDIG_NOKIA_IR60_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) + 1) +#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) + +#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define GRUNDIG2_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define FDC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) // 5%: avoid conflict with NETBOX +#define FDC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5)) +#define FDC_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) +#define FDC_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5)) +#define FDC_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define FDC_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1) +#define FDC_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define FDC_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#if 0 +#define FDC_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) // could be negative: 255 +#else +#define FDC_0_PAUSE_LEN_MIN (1) // simply use 1 +#endif +#define FDC_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RCCAR_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RCCAR_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RCCAR_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RCCAR_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RCCAR_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define RCCAR_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define RCCAR_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define RCCAR_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define RCCAR_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define RCCAR_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) + +#define JVC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define JVC_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MIN_TOLERANCE_40 + 0.5) - 1) // HACK! +#define JVC_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MAX_TOLERANCE_70 + 0.5) - 1) // HACK! +#define JVC_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define JVC_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define JVC_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +// autodetect JVC repetition frame within 50 msec: +#define JVC_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * JVC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + +#define NIKON_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_START_BIT_PAUSE_LEN_MIN ((uint16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_START_BIT_PAUSE_LEN_MAX ((uint16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * NIKON_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + +#define KATHREIN_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_0_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_0_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_SYNC_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_SYNC_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define NETBOX_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define NETBOX_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define NETBOX_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define NETBOX_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define NETBOX_PULSE_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME)) +#define NETBOX_PAUSE_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME)) +#define NETBOX_PULSE_REST_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME / 4)) +#define NETBOX_PAUSE_REST_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME / 4)) + +#define LEGO_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) + +#define AUTO_FRAME_REPETITION_LEN (uint16_t)(F_INTERRUPTS * AUTO_FRAME_REPETITION_TIME + 0.5) // use uint16_t! + +#ifdef ANALYZE +#define ANALYZE_PUTCHAR(a) { if (! silent) { putchar (a); } } +#ifndef LIRC_IRMP +#define ANALYZE_ONLY_NORMAL_PUTCHAR(a) { if (! silent && !verbose) { putchar (a); } } +#else +#define ANALYZE_ONLY_NORMAL_PUTCHAR(a) +#endif +#define ANALYZE_PRINTF(...) { if (verbose) { printf (__VA_ARGS__); } } +#define ANALYZE_NEWLINE() { if (verbose) { putchar ('\n'); } } +static int silent = TRUE; +static int time_counter; +static int verbose; +#else +#define ANALYZE_PUTCHAR(a) +#define ANALYZE_ONLY_NORMAL_PUTCHAR(a) +#define ANALYZE_PRINTF(...) +#define ANALYZE_NEWLINE() +#endif + +#if IRMP_USE_CALLBACK == 1 +static void (*irmp_callback_ptr) (uint8_t); +#endif // IRMP_USE_CALLBACK == 1 + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Protocol names + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_PROTOCOL_NAMES == 1 +char * +irmp_protocol_names[IRMP_N_PROTOCOLS + 1] = +{ + "UNKNOWN", + "SIRCS", + "NEC", + "SAMSUNG", + "MATSUSH", + "KASEIKYO", + "RECS80", + "RC5", + "DENON", + "RC6", + "SAMSG32", + "APPLE", + "RECS80EX", + "NUBERT", + "BANG OLU", + "GRUNDIG", + "NOKIA", + "SIEMENS", + "FDC", + "RCCAR", + "JVC", + "RC6A", + "NIKON", + "RUWIDO", + "IR60", + "KATHREIN", + "NETBOX", + "NEC16", + "NEC42", + "LEGO", + "THOMSON" +}; +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Logging + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_LOGGING == 1 // logging via UART + +#if IRMP_EXT_LOGGING == 1 // use external logging +#include "irmpextlog.h" +#else // normal UART log (IRMP_EXT_LOGGING == 0) +#define BAUD 9600L +#include + +#ifdef UBRR0H + +#define UART0_UBRRH UBRR0H +#define UART0_UBRRL UBRR0L +#define UART0_UCSRA UCSR0A +#define UART0_UCSRB UCSR0B +#define UART0_UCSRC UCSR0C +#define UART0_UDRE_BIT_VALUE (1< ENDBITS) + { // if stop condition is true, output on uart + uint16_t i; + + for (i = 0; i < STARTCYCLES; i++) + { + irmp_uart_putc ('0'); // the ignored starting zeros + } + + for (i = 0; i < (buf_idx - ENDBITS + 20) / 8; i++) // transform bitset into uart chars + { + uint8_t d = buf[i]; + uint8_t j; + + for (j = 0; j < 8; j++) + { + irmp_uart_putc ((d & 1) + '0'); + d >>= 1; + } + } + + irmp_uart_putc ('\n'); + buf_idx = 0; + } + } + else + { + cnt = 0; + } + } + } +} + +#else +#define irmp_log(val) +#endif //IRMP_LOGGING + +typedef struct +{ + uint8_t protocol; // ir protocol + uint8_t pulse_1_len_min; // minimum length of pulse with bit value 1 + uint8_t pulse_1_len_max; // maximum length of pulse with bit value 1 + uint8_t pause_1_len_min; // minimum length of pause with bit value 1 + uint8_t pause_1_len_max; // maximum length of pause with bit value 1 + uint8_t pulse_0_len_min; // minimum length of pulse with bit value 0 + uint8_t pulse_0_len_max; // maximum length of pulse with bit value 0 + uint8_t pause_0_len_min; // minimum length of pause with bit value 0 + uint8_t pause_0_len_max; // maximum length of pause with bit value 0 + uint8_t address_offset; // address offset + uint8_t address_end; // end of address + uint8_t command_offset; // command offset + uint8_t command_end; // end of command + uint8_t complete_len; // complete length of frame + uint8_t stop_bit; // flag: frame has stop bit + uint8_t lsb_first; // flag: LSB first + uint8_t flags; // some flags +} IRMP_PARAMETER; + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER sircs_param = +{ + IRMP_SIRCS_PROTOCOL, // protocol: ir protocol + SIRCS_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + SIRCS_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + SIRCS_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + SIRCS_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + SIRCS_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + SIRCS_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + SIRCS_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + SIRCS_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + SIRCS_ADDRESS_OFFSET, // address_offset: address offset + SIRCS_ADDRESS_OFFSET + SIRCS_ADDRESS_LEN, // address_end: end of address + SIRCS_COMMAND_OFFSET, // command_offset: command offset + SIRCS_COMMAND_OFFSET + SIRCS_COMMAND_LEN, // command_end: end of command + SIRCS_COMPLETE_DATA_LEN, // complete_len: complete length of frame + SIRCS_STOP_BIT, // stop_bit: flag: frame has stop bit + SIRCS_LSB, // lsb_first: flag: LSB first + SIRCS_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nec_param = +{ + IRMP_NEC_PROTOCOL, // protocol: ir protocol + NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NEC_ADDRESS_OFFSET, // address_offset: address offset + NEC_ADDRESS_OFFSET + NEC_ADDRESS_LEN, // address_end: end of address + NEC_COMMAND_OFFSET, // command_offset: command offset + NEC_COMMAND_OFFSET + NEC_COMMAND_LEN, // command_end: end of command + NEC_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NEC_STOP_BIT, // stop_bit: flag: frame has stop bit + NEC_LSB, // lsb_first: flag: LSB first + NEC_FLAGS // flags: some flags +}; + +static const PROGMEM IRMP_PARAMETER nec_rep_param = +{ + IRMP_NEC_PROTOCOL, // protocol: ir protocol + NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + 0, // address_offset: address offset + 0, // address_end: end of address + 0, // command_offset: command offset + 0, // command_end: end of command + 0, // complete_len: complete length of frame + NEC_STOP_BIT, // stop_bit: flag: frame has stop bit + NEC_LSB, // lsb_first: flag: LSB first + NEC_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nec42_param = +{ + IRMP_NEC42_PROTOCOL, // protocol: ir protocol + NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NEC42_ADDRESS_OFFSET, // address_offset: address offset + NEC42_ADDRESS_OFFSET + NEC42_ADDRESS_LEN, // address_end: end of address + NEC42_COMMAND_OFFSET, // command_offset: command offset + NEC42_COMMAND_OFFSET + NEC42_COMMAND_LEN, // command_end: end of command + NEC42_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NEC_STOP_BIT, // stop_bit: flag: frame has stop bit + NEC_LSB, // lsb_first: flag: LSB first + NEC_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER samsung_param = +{ + IRMP_SAMSUNG_PROTOCOL, // protocol: ir protocol + SAMSUNG_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + SAMSUNG_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + SAMSUNG_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + SAMSUNG_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + SAMSUNG_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + SAMSUNG_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + SAMSUNG_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + SAMSUNG_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + SAMSUNG_ADDRESS_OFFSET, // address_offset: address offset + SAMSUNG_ADDRESS_OFFSET + SAMSUNG_ADDRESS_LEN, // address_end: end of address + SAMSUNG_COMMAND_OFFSET, // command_offset: command offset + SAMSUNG_COMMAND_OFFSET + SAMSUNG_COMMAND_LEN, // command_end: end of command + SAMSUNG_COMPLETE_DATA_LEN, // complete_len: complete length of frame + SAMSUNG_STOP_BIT, // stop_bit: flag: frame has stop bit + SAMSUNG_LSB, // lsb_first: flag: LSB first + SAMSUNG_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER matsushita_param = +{ + IRMP_MATSUSHITA_PROTOCOL, // protocol: ir protocol + MATSUSHITA_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + MATSUSHITA_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + MATSUSHITA_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + MATSUSHITA_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + MATSUSHITA_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + MATSUSHITA_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + MATSUSHITA_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + MATSUSHITA_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + MATSUSHITA_ADDRESS_OFFSET, // address_offset: address offset + MATSUSHITA_ADDRESS_OFFSET + MATSUSHITA_ADDRESS_LEN, // address_end: end of address + MATSUSHITA_COMMAND_OFFSET, // command_offset: command offset + MATSUSHITA_COMMAND_OFFSET + MATSUSHITA_COMMAND_LEN, // command_end: end of command + MATSUSHITA_COMPLETE_DATA_LEN, // complete_len: complete length of frame + MATSUSHITA_STOP_BIT, // stop_bit: flag: frame has stop bit + MATSUSHITA_LSB, // lsb_first: flag: LSB first + MATSUSHITA_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER kaseikyo_param = +{ + IRMP_KASEIKYO_PROTOCOL, // protocol: ir protocol + KASEIKYO_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + KASEIKYO_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + KASEIKYO_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + KASEIKYO_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + KASEIKYO_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + KASEIKYO_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + KASEIKYO_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + KASEIKYO_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + KASEIKYO_ADDRESS_OFFSET, // address_offset: address offset + KASEIKYO_ADDRESS_OFFSET + KASEIKYO_ADDRESS_LEN, // address_end: end of address + KASEIKYO_COMMAND_OFFSET, // command_offset: command offset + KASEIKYO_COMMAND_OFFSET + KASEIKYO_COMMAND_LEN, // command_end: end of command + KASEIKYO_COMPLETE_DATA_LEN, // complete_len: complete length of frame + KASEIKYO_STOP_BIT, // stop_bit: flag: frame has stop bit + KASEIKYO_LSB, // lsb_first: flag: LSB first + KASEIKYO_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER recs80_param = +{ + IRMP_RECS80_PROTOCOL, // protocol: ir protocol + RECS80_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + RECS80_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + RECS80_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + RECS80_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + RECS80_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + RECS80_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + RECS80_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + RECS80_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + RECS80_ADDRESS_OFFSET, // address_offset: address offset + RECS80_ADDRESS_OFFSET + RECS80_ADDRESS_LEN, // address_end: end of address + RECS80_COMMAND_OFFSET, // command_offset: command offset + RECS80_COMMAND_OFFSET + RECS80_COMMAND_LEN, // command_end: end of command + RECS80_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RECS80_STOP_BIT, // stop_bit: flag: frame has stop bit + RECS80_LSB, // lsb_first: flag: LSB first + RECS80_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER rc5_param = +{ + IRMP_RC5_PROTOCOL, // protocol: ir protocol + RC5_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + RC5_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + RC5_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + RC5_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + RC5_ADDRESS_OFFSET, // address_offset: address offset + RC5_ADDRESS_OFFSET + RC5_ADDRESS_LEN, // address_end: end of address + RC5_COMMAND_OFFSET, // command_offset: command offset + RC5_COMMAND_OFFSET + RC5_COMMAND_LEN, // command_end: end of command + RC5_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RC5_STOP_BIT, // stop_bit: flag: frame has stop bit + RC5_LSB, // lsb_first: flag: LSB first + RC5_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER denon_param = +{ + IRMP_DENON_PROTOCOL, // protocol: ir protocol + DENON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + DENON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + DENON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + DENON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + DENON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + DENON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + DENON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + DENON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + DENON_ADDRESS_OFFSET, // address_offset: address offset + DENON_ADDRESS_OFFSET + DENON_ADDRESS_LEN, // address_end: end of address + DENON_COMMAND_OFFSET, // command_offset: command offset + DENON_COMMAND_OFFSET + DENON_COMMAND_LEN, // command_end: end of command + DENON_COMPLETE_DATA_LEN, // complete_len: complete length of frame + DENON_STOP_BIT, // stop_bit: flag: frame has stop bit + DENON_LSB, // lsb_first: flag: LSB first + DENON_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER rc6_param = +{ + IRMP_RC6_PROTOCOL, // protocol: ir protocol + + RC6_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + RC6_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + RC6_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + RC6_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + RC6_ADDRESS_OFFSET, // address_offset: address offset + RC6_ADDRESS_OFFSET + RC6_ADDRESS_LEN, // address_end: end of address + RC6_COMMAND_OFFSET, // command_offset: command offset + RC6_COMMAND_OFFSET + RC6_COMMAND_LEN, // command_end: end of command + RC6_COMPLETE_DATA_LEN_SHORT, // complete_len: complete length of frame + RC6_STOP_BIT, // stop_bit: flag: frame has stop bit + RC6_LSB, // lsb_first: flag: LSB first + RC6_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER recs80ext_param = +{ + IRMP_RECS80EXT_PROTOCOL, // protocol: ir protocol + RECS80EXT_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + RECS80EXT_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + RECS80EXT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + RECS80EXT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + RECS80EXT_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + RECS80EXT_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + RECS80EXT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + RECS80EXT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + RECS80EXT_ADDRESS_OFFSET, // address_offset: address offset + RECS80EXT_ADDRESS_OFFSET + RECS80EXT_ADDRESS_LEN, // address_end: end of address + RECS80EXT_COMMAND_OFFSET, // command_offset: command offset + RECS80EXT_COMMAND_OFFSET + RECS80EXT_COMMAND_LEN, // command_end: end of command + RECS80EXT_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RECS80EXT_STOP_BIT, // stop_bit: flag: frame has stop bit + RECS80EXT_LSB, // lsb_first: flag: LSB first + RECS80EXT_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nubert_param = +{ + IRMP_NUBERT_PROTOCOL, // protocol: ir protocol + NUBERT_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NUBERT_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NUBERT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NUBERT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NUBERT_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NUBERT_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NUBERT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NUBERT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NUBERT_ADDRESS_OFFSET, // address_offset: address offset + NUBERT_ADDRESS_OFFSET + NUBERT_ADDRESS_LEN, // address_end: end of address + NUBERT_COMMAND_OFFSET, // command_offset: command offset + NUBERT_COMMAND_OFFSET + NUBERT_COMMAND_LEN, // command_end: end of command + NUBERT_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NUBERT_STOP_BIT, // stop_bit: flag: frame has stop bit + NUBERT_LSB, // lsb_first: flag: LSB first + NUBERT_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER bang_olufsen_param = +{ + IRMP_BANG_OLUFSEN_PROTOCOL, // protocol: ir protocol + BANG_OLUFSEN_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + BANG_OLUFSEN_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + BANG_OLUFSEN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + BANG_OLUFSEN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + BANG_OLUFSEN_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + BANG_OLUFSEN_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + BANG_OLUFSEN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + BANG_OLUFSEN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + BANG_OLUFSEN_ADDRESS_OFFSET, // address_offset: address offset + BANG_OLUFSEN_ADDRESS_OFFSET + BANG_OLUFSEN_ADDRESS_LEN, // address_end: end of address + BANG_OLUFSEN_COMMAND_OFFSET, // command_offset: command offset + BANG_OLUFSEN_COMMAND_OFFSET + BANG_OLUFSEN_COMMAND_LEN, // command_end: end of command + BANG_OLUFSEN_COMPLETE_DATA_LEN, // complete_len: complete length of frame + BANG_OLUFSEN_STOP_BIT, // stop_bit: flag: frame has stop bit + BANG_OLUFSEN_LSB, // lsb_first: flag: LSB first + BANG_OLUFSEN_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + +static uint8_t first_bit; + +static const PROGMEM IRMP_PARAMETER grundig_param = +{ + IRMP_GRUNDIG_PROTOCOL, // protocol: ir protocol + + GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + GRUNDIG_NOKIA_IR60_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + GRUNDIG_NOKIA_IR60_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + GRUNDIG_ADDRESS_OFFSET, // address_offset: address offset + GRUNDIG_ADDRESS_OFFSET + GRUNDIG_ADDRESS_LEN, // address_end: end of address + GRUNDIG_COMMAND_OFFSET, // command_offset: command offset + GRUNDIG_COMMAND_OFFSET + GRUNDIG_COMMAND_LEN + 1, // command_end: end of command (USE 1 bit MORE to STORE NOKIA DATA!) + NOKIA_COMPLETE_DATA_LEN, // complete_len: complete length of frame, here: NOKIA instead of GRUNDIG! + GRUNDIG_NOKIA_IR60_STOP_BIT, // stop_bit: flag: frame has stop bit + GRUNDIG_NOKIA_IR60_LSB, // lsb_first: flag: LSB first + GRUNDIG_NOKIA_IR60_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER ruwido_param = +{ + IRMP_RUWIDO_PROTOCOL, // protocol: ir protocol + SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + RUWIDO_ADDRESS_OFFSET, // address_offset: address offset + RUWIDO_ADDRESS_OFFSET + RUWIDO_ADDRESS_LEN, // address_end: end of address + RUWIDO_COMMAND_OFFSET, // command_offset: command offset + RUWIDO_COMMAND_OFFSET + RUWIDO_COMMAND_LEN, // command_end: end of command + SIEMENS_COMPLETE_DATA_LEN, // complete_len: complete length of frame, here: SIEMENS instead of RUWIDO! + SIEMENS_OR_RUWIDO_STOP_BIT, // stop_bit: flag: frame has stop bit + SIEMENS_OR_RUWIDO_LSB, // lsb_first: flag: LSB first + SIEMENS_OR_RUWIDO_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_GRUNDIG2_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER grundig2_param = +{ + IRMP_GRUNDIG2_PROTOCOL, // protocol: ir protocol + GRUNDIG2_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + GRUNDIG2_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + GRUNDIG2_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + GRUNDIG2_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + GRUNDIG2_ADDRESS_OFFSET, // address_offset: address offset + GRUNDIG2_ADDRESS_OFFSET + GRUNDIG2_ADDRESS_LEN, // address_end: end of address + GRUNDIG2_COMMAND_OFFSET, // command_offset: command offset + GRUNDIG2_COMMAND_OFFSET + GRUNDIG2_COMMAND_LEN, // command_end: end of command + GRUNDIG2_COMPLETE_DATA_LEN, // complete_len: complete length of frame + GRUNDIG2_STOP_BIT, // stop_bit: flag: frame has stop bit + GRUNDIG2_LSB, // lsb_first: flag: LSB first + GRUNDIG2_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER fdc_param = +{ + IRMP_FDC_PROTOCOL, // protocol: ir protocol + FDC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + FDC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + FDC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + FDC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + FDC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + FDC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + FDC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + FDC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + FDC_ADDRESS_OFFSET, // address_offset: address offset + FDC_ADDRESS_OFFSET + FDC_ADDRESS_LEN, // address_end: end of address + FDC_COMMAND_OFFSET, // command_offset: command offset + FDC_COMMAND_OFFSET + FDC_COMMAND_LEN, // command_end: end of command + FDC_COMPLETE_DATA_LEN, // complete_len: complete length of frame + FDC_STOP_BIT, // stop_bit: flag: frame has stop bit + FDC_LSB, // lsb_first: flag: LSB first + FDC_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER rccar_param = +{ + IRMP_RCCAR_PROTOCOL, // protocol: ir protocol + RCCAR_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + RCCAR_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + RCCAR_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + RCCAR_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + RCCAR_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + RCCAR_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + RCCAR_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + RCCAR_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + RCCAR_ADDRESS_OFFSET, // address_offset: address offset + RCCAR_ADDRESS_OFFSET + RCCAR_ADDRESS_LEN, // address_end: end of address + RCCAR_COMMAND_OFFSET, // command_offset: command offset + RCCAR_COMMAND_OFFSET + RCCAR_COMMAND_LEN, // command_end: end of command + RCCAR_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RCCAR_STOP_BIT, // stop_bit: flag: frame has stop bit + RCCAR_LSB, // lsb_first: flag: LSB first + RCCAR_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nikon_param = +{ + IRMP_NIKON_PROTOCOL, // protocol: ir protocol + NIKON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NIKON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NIKON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NIKON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NIKON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NIKON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NIKON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NIKON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NIKON_ADDRESS_OFFSET, // address_offset: address offset + NIKON_ADDRESS_OFFSET + NIKON_ADDRESS_LEN, // address_end: end of address + NIKON_COMMAND_OFFSET, // command_offset: command offset + NIKON_COMMAND_OFFSET + NIKON_COMMAND_LEN, // command_end: end of command + NIKON_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NIKON_STOP_BIT, // stop_bit: flag: frame has stop bit + NIKON_LSB, // lsb_first: flag: LSB first + NIKON_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER kathrein_param = +{ + IRMP_KATHREIN_PROTOCOL, // protocol: ir protocol + KATHREIN_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + KATHREIN_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + KATHREIN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + KATHREIN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + KATHREIN_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + KATHREIN_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + KATHREIN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + KATHREIN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + KATHREIN_ADDRESS_OFFSET, // address_offset: address offset + KATHREIN_ADDRESS_OFFSET + KATHREIN_ADDRESS_LEN, // address_end: end of address + KATHREIN_COMMAND_OFFSET, // command_offset: command offset + KATHREIN_COMMAND_OFFSET + KATHREIN_COMMAND_LEN, // command_end: end of command + KATHREIN_COMPLETE_DATA_LEN, // complete_len: complete length of frame + KATHREIN_STOP_BIT, // stop_bit: flag: frame has stop bit + KATHREIN_LSB, // lsb_first: flag: LSB first + KATHREIN_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER netbox_param = +{ + IRMP_NETBOX_PROTOCOL, // protocol: ir protocol + NETBOX_PULSE_LEN, // pulse_1_len_min: minimum length of pulse with bit value 1, here: exact value + NETBOX_PULSE_REST_LEN, // pulse_1_len_max: maximum length of pulse with bit value 1, here: rest value + NETBOX_PAUSE_LEN, // pause_1_len_min: minimum length of pause with bit value 1, here: exact value + NETBOX_PAUSE_REST_LEN, // pause_1_len_max: maximum length of pause with bit value 1, here: rest value + NETBOX_PULSE_LEN, // pulse_0_len_min: minimum length of pulse with bit value 0, here: exact value + NETBOX_PULSE_REST_LEN, // pulse_0_len_max: maximum length of pulse with bit value 0, here: rest value + NETBOX_PAUSE_LEN, // pause_0_len_min: minimum length of pause with bit value 0, here: exact value + NETBOX_PAUSE_REST_LEN, // pause_0_len_max: maximum length of pause with bit value 0, here: rest value + NETBOX_ADDRESS_OFFSET, // address_offset: address offset + NETBOX_ADDRESS_OFFSET + NETBOX_ADDRESS_LEN, // address_end: end of address + NETBOX_COMMAND_OFFSET, // command_offset: command offset + NETBOX_COMMAND_OFFSET + NETBOX_COMMAND_LEN, // command_end: end of command + NETBOX_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NETBOX_STOP_BIT, // stop_bit: flag: frame has stop bit + NETBOX_LSB, // lsb_first: flag: LSB first + NETBOX_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER lego_param = +{ + IRMP_LEGO_PROTOCOL, // protocol: ir protocol + LEGO_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + LEGO_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + LEGO_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + LEGO_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + LEGO_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + LEGO_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + LEGO_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + LEGO_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + LEGO_ADDRESS_OFFSET, // address_offset: address offset + LEGO_ADDRESS_OFFSET + LEGO_ADDRESS_LEN, // address_end: end of address + LEGO_COMMAND_OFFSET, // command_offset: command offset + LEGO_COMMAND_OFFSET + LEGO_COMMAND_LEN, // command_end: end of command + LEGO_COMPLETE_DATA_LEN, // complete_len: complete length of frame + LEGO_STOP_BIT, // stop_bit: flag: frame has stop bit + LEGO_LSB, // lsb_first: flag: LSB first + LEGO_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER thomson_param = +{ + IRMP_THOMSON_PROTOCOL, // protocol: ir protocol + THOMSON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + THOMSON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + THOMSON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + THOMSON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + THOMSON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + THOMSON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + THOMSON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + THOMSON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + THOMSON_ADDRESS_OFFSET, // address_offset: address offset + THOMSON_ADDRESS_OFFSET + THOMSON_ADDRESS_LEN, // address_end: end of address + THOMSON_COMMAND_OFFSET, // command_offset: command offset + THOMSON_COMMAND_OFFSET + THOMSON_COMMAND_LEN, // command_end: end of command + THOMSON_COMPLETE_DATA_LEN, // complete_len: complete length of frame + THOMSON_STOP_BIT, // stop_bit: flag: frame has stop bit + THOMSON_LSB, // lsb_first: flag: LSB first + THOMSON_FLAGS // flags: some flags +}; + +#endif + +static uint8_t irmp_bit; // current bit position +static IRMP_PARAMETER irmp_param; + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) +static IRMP_PARAMETER irmp_param2; +#endif + +static volatile uint8_t irmp_ir_detected; +static volatile uint8_t irmp_protocol; +static volatile uint16_t irmp_address; +static volatile uint16_t irmp_command; +static volatile uint16_t irmp_id; // only used for SAMSUNG protocol +static volatile uint8_t irmp_flags; +// static volatile uint8_t irmp_busy_flag; + +#ifdef ANALYZE +static uint8_t IRMP_PIN; +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Initialize IRMP decoder + * @details Configures IRMP input pin + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef ANALYZE +void +irmp_init (void) +{ +#ifndef ARDUINO +#if !defined(PIC_CCS_COMPILER) && !defined(PIC_C18) // only AVR + IRMP_PORT &= ~(1<> 8) == (~irmp_command & 0x00FF)) + { + irmp_command &= 0xff; + irmp_command |= irmp_id << 8; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + case IRMP_NEC_PROTOCOL: + if ((irmp_command >> 8) == (~irmp_command & 0x00FF)) + { + irmp_command &= 0xff; + rtc = TRUE; + } + else if (irmp_address == 0x87EE) + { + ANALYZE_PRINTF ("Switching to APPLE protocol\n"); + irmp_protocol = IRMP_APPLE_PROTOCOL; + irmp_address = (irmp_command & 0xFF00) >> 8; + irmp_command &= 0x00FF; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + case IRMP_SIEMENS_PROTOCOL: + case IRMP_RUWIDO_PROTOCOL: + if (((irmp_command >> 1) & 0x0001) == (~irmp_command & 0x0001)) + { + irmp_command >>= 1; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_GRUNDIG2_PROTOCOL == 1 + case IRMP_GRUNDIG2_PROTOCOL: + if (irmp_command & 0x0001) + { + irmp_command >>= 1; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + case IRMP_KATHREIN_PROTOCOL: + if (irmp_command != 0x0000) + { + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + case IRMP_RC5_PROTOCOL: + irmp_address &= ~0x20; // clear toggle bit + rtc = TRUE; + break; +#endif +#if IRMP_SUPPORT_IR60_PROTOCOL == 1 + case IRMP_IR60_PROTOCOL: + if (irmp_command != 0x007d) // 0x007d (== 62<<1 + 1) is start instruction frame + { + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + case IRMP_RCCAR_PROTOCOL: + // frame in irmp_data: + // Bit 12 11 10 9 8 7 6 5 4 3 2 1 0 + // V D7 D6 D5 D4 D3 D2 D1 D0 A1 A0 C1 C0 // 10 9 8 7 6 5 4 3 2 1 0 + irmp_address = (irmp_command & 0x000C) >> 2; // addr: 0 0 0 0 0 0 0 0 0 A1 A0 + irmp_command = ((irmp_command & 0x1000) >> 2) | // V-Bit: V 0 0 0 0 0 0 0 0 0 0 + ((irmp_command & 0x0003) << 8) | // C-Bits: 0 C1 C0 0 0 0 0 0 0 0 0 + ((irmp_command & 0x0FF0) >> 4); // D-Bits: D7 D6 D5 D4 D3 D2 D1 D0 + rtc = TRUE; // Summe: V C1 C0 D7 D6 D5 D4 D3 D2 D1 D0 + break; +#endif + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 // squeeze code to 8 bit, upper bit indicates release-key + case IRMP_NETBOX_PROTOCOL: + if (irmp_command & 0x1000) // last bit set? + { + if ((irmp_command & 0x1f) == 0x15) // key pressed: 101 01 (LSB) + { + irmp_command >>= 5; + irmp_command &= 0x7F; + rtc = TRUE; + } + else if ((irmp_command & 0x1f) == 0x10) // key released: 000 01 (LSB) + { + irmp_command >>= 5; + irmp_command |= 0x80; + rtc = TRUE; + } + else + { + ANALYZE_PRINTF("error NETBOX: bit6/7 must be 0/1\n"); + } + } + else + { + ANALYZE_PRINTF("error NETBOX: last bit not set\n"); + } + break; +#endif +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 + case IRMP_LEGO_PROTOCOL: + { + uint8_t crc = 0x0F ^ ((irmp_command & 0xF000) >> 12) ^ ((irmp_command & 0x0F00) >> 8) ^ ((irmp_command & 0x00F0) >> 4); + + if ((irmp_command & 0x000F) == crc) + { + irmp_command >>= 4; + rtc = TRUE; + } + else + { + ANALYZE_PRINTF ("CRC error in LEGO protocol\n"); + rtc = TRUE; + } + break; + } +#endif + default: + rtc = TRUE; + } + + if (rtc) + { + irmp_data_p->protocol = irmp_protocol; + irmp_data_p->address = irmp_address; + irmp_data_p->command = irmp_command; + irmp_data_p->flags = irmp_flags; + irmp_command = 0; + irmp_address = 0; + irmp_flags = 0; + } + + irmp_ir_detected = FALSE; + } + + return rtc; +} + +// uint8_t +// irmp_is_busy (void) +// { +// return irmp_busy_flag; +// } + +#if IRMP_USE_CALLBACK == 1 +void +irmp_set_callback_ptr (void (*cb)(uint8_t)) +{ + irmp_callback_ptr = cb; +} +#endif // IRMP_USE_CALLBACK == 1 + +// these statics must not be volatile, because they are only used by irmp_store_bit(), which is called by irmp_ISR() +static uint16_t irmp_tmp_address; // ir address +static uint16_t irmp_tmp_command; // ir command + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1 +static uint16_t irmp_tmp_address2; // ir address +static uint16_t irmp_tmp_command2; // ir command +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 +static uint16_t irmp_tmp_id; // ir id (only SAMSUNG) +#endif +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 +static uint8_t xor_check[6]; // check kaseikyo "parity" bits +static uint8_t genre2; // save genre2 bits here, later copied to MSB in flags +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * store bit + * @details store bit in temp address or temp command + * @param value to store: 0 or 1 + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +// verhindert, dass irmp_store_bit() inline compiliert wird: +// static void irmp_store_bit (uint8_t) __attribute__ ((noinline)); + +static void +irmp_store_bit (uint8_t value) +{ +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + if (irmp_bit == 0 && irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL) + { + first_bit = value; + } + else +#endif + + if (irmp_bit >= irmp_param.address_offset && irmp_bit < irmp_param.address_end) + { + if (irmp_param.lsb_first) + { + irmp_tmp_address |= (((uint16_t) (value)) << (irmp_bit - irmp_param.address_offset)); // CV wants cast + } + else + { + irmp_tmp_address <<= 1; + irmp_tmp_address |= value; + } + } + else if (irmp_bit >= irmp_param.command_offset && irmp_bit < irmp_param.command_end) + { + if (irmp_param.lsb_first) + { + irmp_tmp_command |= (((uint16_t) (value)) << (irmp_bit - irmp_param.command_offset)); // CV wants cast + } + else + { + irmp_tmp_command <<= 1; + irmp_tmp_command |= value; + } + } + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit >= 13 && irmp_bit < 26) + { + irmp_tmp_address2 |= (((uint16_t) (value)) << (irmp_bit - 13)); // CV wants cast + } +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit >= SAMSUNG_ID_OFFSET && irmp_bit < SAMSUNG_ID_OFFSET + SAMSUNG_ID_LEN) + { + irmp_tmp_id |= (((uint16_t) (value)) << (irmp_bit - SAMSUNG_ID_OFFSET)); // store with LSB first + } +#endif + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL) + { + if (irmp_bit >= 20 && irmp_bit < 24) + { + irmp_tmp_command |= (((uint16_t) (value)) << (irmp_bit - 8)); // store 4 system bits (genre 1) in upper nibble with LSB first + } + else if (irmp_bit >= 24 && irmp_bit < 28) + { + genre2 |= (((uint8_t) (value)) << (irmp_bit - 20)); // store 4 system bits (genre 2) in upper nibble with LSB first + } + + if (irmp_bit < KASEIKYO_COMPLETE_DATA_LEN) + { + if (value) + { + xor_check[irmp_bit / 8] |= 1 << (irmp_bit % 8); + } + else + { + xor_check[irmp_bit / 8] &= ~(1 << (irmp_bit % 8)); + } + } + } +#endif + + irmp_bit++; +} + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * store bit + * @details store bit in temp address or temp command + * @param value to store: 0 or 1 + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) +static void +irmp_store_bit2 (uint8_t value) +{ + uint8_t irmp_bit2; + + if (irmp_param.protocol) + { + irmp_bit2 = irmp_bit - 2; + } + else + { + irmp_bit2 = irmp_bit - 1; + } + + if (irmp_bit2 >= irmp_param2.address_offset && irmp_bit2 < irmp_param2.address_end) + { + irmp_tmp_address2 |= (((uint16_t) (value)) << (irmp_bit2 - irmp_param2.address_offset)); // CV wants cast + } + else if (irmp_bit2 >= irmp_param2.command_offset && irmp_bit2 < irmp_param2.command_end) + { + irmp_tmp_command2 |= (((uint16_t) (value)) << (irmp_bit2 - irmp_param2.command_offset)); // CV wants cast + } +} +#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * ISR routine + * @details ISR routine, called 10000 times per second + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +uint8_t +irmp_ISR (uint8_t x42) +{ + static uint8_t irmp_start_bit_detected; // flag: start bit detected + static uint8_t wait_for_space; // flag: wait for data bit space + static uint8_t wait_for_start_space; // flag: wait for start bit space + static uint8_t irmp_pulse_time; // count bit time for pulse + static PAUSE_LEN irmp_pause_time; // count bit time for pause + static uint16_t last_irmp_address = 0xFFFF; // save last irmp address to recognize key repetition + static uint16_t last_irmp_command = 0xFFFF; // save last irmp command to recognize key repetition + static uint16_t repetition_len; // SIRCS repeats frame 2-5 times with 45 ms pause + static uint8_t repetition_frame_number; +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + static uint16_t last_irmp_denon_command; // save last irmp command to recognize DENON frame repetition +#endif +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + static uint8_t rc5_cmd_bit6; // bit 6 of RC5 command is the inverted 2nd start bit +#endif +#if IRMP_SUPPORT_MANCHESTER == 1 + static PAUSE_LEN last_pause; // last pause value +#endif +#if IRMP_SUPPORT_MANCHESTER == 1 || IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + static uint8_t last_value; // last bit value +#endif + uint8_t irmp_input; // input value + +#ifdef ANALYZE + time_counter++; +#endif + + irmp_input = input(x42); + +#if IRMP_USE_CALLBACK == 1 + if (irmp_callback_ptr) + { + static uint8_t last_inverted_input; + + if (last_inverted_input != !irmp_input) + { + (*irmp_callback_ptr) (! irmp_input); + last_inverted_input = !irmp_input; + } + } +#endif // IRMP_USE_CALLBACK == 1 + + irmp_log(irmp_input); // log ir signal, if IRMP_LOGGING defined + + if (! irmp_ir_detected) // ir code already detected? + { // no... + if (! irmp_start_bit_detected) // start bit detected? + { // no... + if (! irmp_input) // receiving burst? + { // yes... +// irmp_busy_flag = TRUE; +#ifdef ANALYZE + if (! irmp_pulse_time) + { + ANALYZE_PRINTF("%8.3fms [starting pulse]\n", (double) (time_counter * 1000) / F_INTERRUPTS); + } +#endif + irmp_pulse_time++; // increment counter + } + else + { // no... + if (irmp_pulse_time) // it's dark.... + { // set flags for counting the time of darkness... + irmp_start_bit_detected = 1; + wait_for_start_space = 1; + wait_for_space = 0; + irmp_tmp_command = 0; + irmp_tmp_address = 0; +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + genre2 = 0; +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1 + irmp_tmp_command2 = 0; + irmp_tmp_address2 = 0; +#endif + + irmp_bit = 0xff; + irmp_pause_time = 1; // 1st pause: set to 1, not to 0! +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + rc5_cmd_bit6 = 0; // fm 2010-03-07: bugfix: reset it after incomplete RC5 frame! +#endif + } + else + { + if (repetition_len < 0xFFFF) // avoid overflow of counter + { + repetition_len++; + } + } + } + } + else + { + if (wait_for_start_space) // we have received start bit... + { // ...and are counting the time of darkness + if (irmp_input) // still dark? + { // yes + irmp_pause_time++; // increment counter + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 + if (((irmp_pulse_time < NIKON_START_BIT_PULSE_LEN_MIN || irmp_pulse_time > NIKON_START_BIT_PULSE_LEN_MAX) && irmp_pause_time > IRMP_TIMEOUT_LEN) || + irmp_pause_time > IRMP_TIMEOUT_NIKON_LEN) +#else + if (irmp_pause_time > IRMP_TIMEOUT_LEN) // timeout? +#endif + { // yes... +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL) // don't show eror if JVC protocol, irmp_pulse_time has been set below! + { + ; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + { + ANALYZE_PRINTF ("%8.3fms error 1: pause after start bit pulse %d too long: %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); + } +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags, let's wait for another start bit + irmp_pulse_time = 0; + irmp_pause_time = 0; + } + } + else + { // receiving first data pulse! + IRMP_PARAMETER * irmp_param_p = (IRMP_PARAMETER *) 0; + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + irmp_param2.protocol = 0; +#endif + + ANALYZE_PRINTF ("%8.3fms [start-bit: pulse = %2d, pause = %2d]\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time); + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + if (irmp_pulse_time >= SIRCS_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIRCS_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= SIRCS_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIRCS_START_BIT_PAUSE_LEN_MAX) + { // it's SIRCS + ANALYZE_PRINTF ("protocol = SIRCS, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + SIRCS_START_BIT_PULSE_LEN_MIN, SIRCS_START_BIT_PULSE_LEN_MAX, + SIRCS_START_BIT_PAUSE_LEN_MIN, SIRCS_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) (IRMP_PARAMETER *) &sircs_param; + } + else +#endif // IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL && // last protocol was JVC, awaiting repeat frame + irmp_pulse_time >= JVC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= JVC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= JVC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= JVC_REPEAT_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = NEC or JVC (type 1) repeat frame, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + JVC_START_BIT_PULSE_LEN_MIN, JVC_START_BIT_PULSE_LEN_MAX, + JVC_REPEAT_START_BIT_PAUSE_LEN_MIN, JVC_REPEAT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX) + { +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + ANALYZE_PRINTF ("protocol = NEC42, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec42_param; +#else + ANALYZE_PRINTF ("protocol = NEC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; +#endif + + } + else if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NEC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_REPEAT_START_BIT_PAUSE_LEN_MAX) + { // it's NEC +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL) // last protocol was JVC, awaiting repeat frame + { // some jvc remote controls use nec repetition frame for jvc repetition frame + ANALYZE_PRINTF ("protocol = JVC repeat frame type 2, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + { + ANALYZE_PRINTF ("protocol = NEC (repetition frame), start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX); + + irmp_param_p = (IRMP_PARAMETER *) &nec_rep_param; + } + } + else + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL && // last protocol was JVC, awaiting repeat frame + irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NEC_0_PAUSE_LEN_MIN && irmp_pause_time <= NEC_0_PAUSE_LEN_MAX) + { // it's JVC repetition type 3 + ANALYZE_PRINTF ("protocol = JVC repeat frame type 3, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + +#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1 + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 + if (irmp_pulse_time >= NIKON_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NIKON_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NIKON_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NIKON_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = NIKON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NIKON_START_BIT_PULSE_LEN_MIN, NIKON_START_BIT_PULSE_LEN_MAX, + NIKON_START_BIT_PAUSE_LEN_MIN, NIKON_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nikon_param; + } + else +#endif // IRMP_SUPPORT_NIKON_PROTOCOL == 1 + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + if (irmp_pulse_time >= SAMSUNG_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX) + { // it's SAMSUNG + ANALYZE_PRINTF ("protocol = SAMSUNG, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + SAMSUNG_START_BIT_PULSE_LEN_MIN, SAMSUNG_START_BIT_PULSE_LEN_MAX, + SAMSUNG_START_BIT_PAUSE_LEN_MIN, SAMSUNG_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &samsung_param; + } + else +#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + +#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1 + if (irmp_pulse_time >= MATSUSHITA_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MATSUSHITA_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= MATSUSHITA_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MATSUSHITA_START_BIT_PAUSE_LEN_MAX) + { // it's MATSUSHITA + ANALYZE_PRINTF ("protocol = MATSUSHITA, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + MATSUSHITA_START_BIT_PULSE_LEN_MIN, MATSUSHITA_START_BIT_PULSE_LEN_MAX, + MATSUSHITA_START_BIT_PAUSE_LEN_MIN, MATSUSHITA_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &matsushita_param; + } + else +#endif // IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1 + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + if (irmp_pulse_time >= KASEIKYO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KASEIKYO_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= KASEIKYO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KASEIKYO_START_BIT_PAUSE_LEN_MAX) + { // it's KASEIKYO + ANALYZE_PRINTF ("protocol = KASEIKYO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + KASEIKYO_START_BIT_PULSE_LEN_MIN, KASEIKYO_START_BIT_PULSE_LEN_MAX, + KASEIKYO_START_BIT_PAUSE_LEN_MIN, KASEIKYO_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &kaseikyo_param; + } + else +#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + +#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 + if (irmp_pulse_time >= RECS80_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RECS80_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80_START_BIT_PAUSE_LEN_MAX) + { // it's RECS80 + ANALYZE_PRINTF ("protocol = RECS80, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RECS80_START_BIT_PULSE_LEN_MIN, RECS80_START_BIT_PULSE_LEN_MAX, + RECS80_START_BIT_PAUSE_LEN_MIN, RECS80_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &recs80_param; + } + else +#endif // IRMP_SUPPORT_RECS80_PROTOCOL == 1 + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + if (((irmp_pulse_time >= RC5_START_BIT_LEN_MIN && irmp_pulse_time <= RC5_START_BIT_LEN_MAX) || + (irmp_pulse_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX)) && + ((irmp_pause_time >= RC5_START_BIT_LEN_MIN && irmp_pause_time <= RC5_START_BIT_LEN_MAX) || + (irmp_pause_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX))) + { // it's RC5 +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = RC5 or FDC\n"); + ANALYZE_PRINTF ("FDC start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, + FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX); + memcpy_P (&irmp_param2, &fdc_param, sizeof (IRMP_PARAMETER)); + } + else +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 + +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = RC5 or RCCAR\n"); + ANALYZE_PRINTF ("RCCAR start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX, + RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX); + memcpy_P (&irmp_param2, &rccar_param, sizeof (IRMP_PARAMETER)); + } + else +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + { + ANALYZE_PRINTF ("protocol = RC5, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or pulse: %3d - %3d, pause: %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + 2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX, + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + 2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX); + } + + irmp_param_p = (IRMP_PARAMETER *) &rc5_param; + last_pause = irmp_pause_time; + + if ((irmp_pulse_time > RC5_START_BIT_LEN_MAX && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX) || + (irmp_pause_time > RC5_START_BIT_LEN_MAX && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX)) + { + last_value = 0; + rc5_cmd_bit6 = 1<<6; + } + else + { + last_value = 1; + } + } + else +#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1 + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + if ( (irmp_pulse_time >= DENON_PULSE_LEN_MIN && irmp_pulse_time <= DENON_PULSE_LEN_MAX) && + ((irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX))) + { // it's DENON + ANALYZE_PRINTF ("protocol = DENON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, + DENON_1_PAUSE_LEN_MIN, DENON_1_PAUSE_LEN_MAX, + DENON_0_PAUSE_LEN_MIN, DENON_0_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &denon_param; + } + else +#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1 + +#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + if ( (irmp_pulse_time >= THOMSON_PULSE_LEN_MIN && irmp_pulse_time <= THOMSON_PULSE_LEN_MAX) && + ((irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX))) + { // it's THOMSON + ANALYZE_PRINTF ("protocol = THOMSON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, + THOMSON_1_PAUSE_LEN_MIN, THOMSON_1_PAUSE_LEN_MAX, + THOMSON_0_PAUSE_LEN_MIN, THOMSON_0_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &thomson_param; + } + else +#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_pulse_time >= RC6_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RC6_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RC6_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RC6_START_BIT_PAUSE_LEN_MAX) + { // it's RC6 + ANALYZE_PRINTF ("protocol = RC6, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RC6_START_BIT_PULSE_LEN_MIN, RC6_START_BIT_PULSE_LEN_MAX, + RC6_START_BIT_PAUSE_LEN_MIN, RC6_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &rc6_param; + last_pause = 0; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + +#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 + if (irmp_pulse_time >= RECS80EXT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80EXT_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RECS80EXT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80EXT_START_BIT_PAUSE_LEN_MAX) + { // it's RECS80EXT + ANALYZE_PRINTF ("protocol = RECS80EXT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RECS80EXT_START_BIT_PULSE_LEN_MIN, RECS80EXT_START_BIT_PULSE_LEN_MAX, + RECS80EXT_START_BIT_PAUSE_LEN_MIN, RECS80EXT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &recs80ext_param; + } + else +#endif // IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 + +#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + if (irmp_pulse_time >= NUBERT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NUBERT_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NUBERT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NUBERT_START_BIT_PAUSE_LEN_MAX) + { // it's NUBERT + ANALYZE_PRINTF ("protocol = NUBERT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NUBERT_START_BIT_PULSE_LEN_MIN, NUBERT_START_BIT_PULSE_LEN_MAX, + NUBERT_START_BIT_PAUSE_LEN_MIN, NUBERT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nubert_param; + } + else +#endif // IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + if (irmp_pulse_time >= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX && + irmp_pause_time >= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX) + { // it's BANG_OLUFSEN + ANALYZE_PRINTF ("protocol = BANG_OLUFSEN\n"); + ANALYZE_PRINTF ("start bit 1 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("start bit 2 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("start bit 3 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("start bit 4 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &bang_olufsen_param; + last_value = 0; + } + else +#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + if (irmp_pulse_time >= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN && irmp_pulse_time <= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX && + irmp_pause_time >= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN && irmp_pause_time <= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX) + { // it's GRUNDIG + ANALYZE_PRINTF ("protocol = GRUNDIG, pre bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX, + GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN, GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &grundig_param; + last_pause = irmp_pause_time; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + if (((irmp_pulse_time >= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX) || + (irmp_pulse_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX)) && + ((irmp_pause_time >= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX) || + (irmp_pause_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX))) + { // it's RUWIDO or SIEMENS + ANALYZE_PRINTF ("protocol = RUWIDO, start bit timings: pulse: %3d - %3d or %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX, + SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &ruwido_param; + last_pause = irmp_pause_time; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + +#if IRMP_SUPPORT_GRUNDIG2_PROTOCOL == 1 + if ((irmp_pulse_time >= GRUNDIG2_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= GRUNDIG2_START_BIT_PULSE_LEN_MAX) && + (irmp_pause_time >= GRUNDIG2_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= GRUNDIG2_START_BIT_PAUSE_LEN_MAX)) + { // it's GRUNDIG2 + ANALYZE_PRINTF ("protocol = GRUNDIG2, start bit timings: pulse: %3d - %3d or %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + GRUNDIG2_START_BIT_PULSE_LEN_MIN, GRUNDIG2_START_BIT_PULSE_LEN_MAX, + 2 * GRUNDIG2_START_BIT_PULSE_LEN_MIN, 2 * GRUNDIG2_START_BIT_PULSE_LEN_MAX, + GRUNDIG2_START_BIT_PAUSE_LEN_MIN, GRUNDIG2_START_BIT_PAUSE_LEN_MAX, + 2 * GRUNDIG2_START_BIT_PAUSE_LEN_MIN, 2 * GRUNDIG2_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &grundig2_param; + last_pause = irmp_pause_time; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = FDC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, + FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &fdc_param; + } + else +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 + +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = RCCAR, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX, + RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &rccar_param; + } + else +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + +#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + if (irmp_pulse_time >= KATHREIN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX) + { // it's KATHREIN + ANALYZE_PRINTF ("protocol = KATHREIN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + KATHREIN_START_BIT_PULSE_LEN_MIN, KATHREIN_START_BIT_PULSE_LEN_MAX, + KATHREIN_START_BIT_PAUSE_LEN_MIN, KATHREIN_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &kathrein_param; + } + else +#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 + if (irmp_pulse_time >= NETBOX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NETBOX_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NETBOX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NETBOX_START_BIT_PAUSE_LEN_MAX) + { // it's NETBOX + ANALYZE_PRINTF ("protocol = NETBOX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NETBOX_START_BIT_PULSE_LEN_MIN, NETBOX_START_BIT_PULSE_LEN_MAX, + NETBOX_START_BIT_PAUSE_LEN_MIN, NETBOX_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &netbox_param; + } + else +#endif // IRMP_SUPPORT_NETBOX_PROTOCOL == 1 + +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 + if (irmp_pulse_time >= LEGO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= LEGO_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= LEGO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= LEGO_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = LEGO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + LEGO_START_BIT_PULSE_LEN_MIN, LEGO_START_BIT_PULSE_LEN_MAX, + LEGO_START_BIT_PAUSE_LEN_MIN, LEGO_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &lego_param; + } + else +#endif // IRMP_SUPPORT_LEGO_PROTOCOL == 1 + + { + ANALYZE_PRINTF ("protocol = UNKNOWN\n"); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // wait for another start bit... + } + + if (irmp_start_bit_detected) + { + memcpy_P (&irmp_param, irmp_param_p, sizeof (IRMP_PARAMETER)); + +#ifdef ANALYZE + if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) + { + ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max); + ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max); + } + else + { + ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max, + 2 * irmp_param.pulse_1_len_min, 2 * irmp_param.pulse_1_len_max); + ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max, + 2 * irmp_param.pause_1_len_min, 2 * irmp_param.pause_1_len_max); + } + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + if (irmp_param2.protocol) + { + ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param2.pulse_0_len_min, irmp_param2.pulse_0_len_max); + ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param2.pause_0_len_min, irmp_param2.pause_0_len_max); + ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param2.pulse_1_len_min, irmp_param2.pulse_1_len_max); + ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param2.pause_1_len_min, irmp_param2.pause_1_len_max); + } +#endif + + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL) + { + ANALYZE_PRINTF ("pulse_toggle: %3d - %3d\n", RC6_TOGGLE_BIT_LEN_MIN, RC6_TOGGLE_BIT_LEN_MAX); + } +#endif + + if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) + { + ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max); + ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max); + } + else + { + ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max, + 2 * irmp_param.pulse_0_len_min, 2 * irmp_param.pulse_0_len_max); + ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max, + 2 * irmp_param.pause_0_len_min, 2 * irmp_param.pause_0_len_max); + } + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL) + { + ANALYZE_PRINTF ("pulse_r: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max); + ANALYZE_PRINTF ("pause_r: %3d - %3d\n", BANG_OLUFSEN_R_PAUSE_LEN_MIN, BANG_OLUFSEN_R_PAUSE_LEN_MAX); + } +#endif + + ANALYZE_PRINTF ("command_offset: %2d\n", irmp_param.command_offset); + ANALYZE_PRINTF ("command_len: %3d\n", irmp_param.command_end - irmp_param.command_offset); + ANALYZE_PRINTF ("complete_len: %3d\n", irmp_param.complete_len); + ANALYZE_PRINTF ("stop_bit: %3d\n", irmp_param.stop_bit); +#endif // ANALYZE + } + + irmp_bit = 0; + +#if IRMP_SUPPORT_MANCHESTER == 1 + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) && + irmp_param.protocol != IRMP_RUWIDO_PROTOCOL && // Manchester, but not RUWIDO + irmp_param.protocol != IRMP_RC6_PROTOCOL) // Manchester, but not RC6 + { + if (irmp_pause_time > irmp_param.pulse_1_len_max && irmp_pause_time <= 2 * irmp_param.pulse_1_len_max) + { + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1'); + ANALYZE_NEWLINE (); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1); + } + else if (! last_value) // && irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max) + { + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); + + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0'); + ANALYZE_NEWLINE (); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0); + } + } + else +#endif // IRMP_SUPPORT_MANCHESTER == 1 + +#if IRMP_SUPPORT_SERIAL == 1 + if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) + { + ; // do nothing + } + else +#endif // IRMP_SUPPORT_SERIAL == 1 + + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_DENON_PROTOCOL) + { + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); + + if (irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) + { // pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); // yes, store 1 + ANALYZE_NEWLINE (); + irmp_store_bit (1); + } + else // if (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX) + { // pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); // yes, store 0 + ANALYZE_NEWLINE (); + irmp_store_bit (0); + } + } + else +#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1 +#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_THOMSON_PROTOCOL) + { + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); + + if (irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) + { // pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); // yes, store 1 + ANALYZE_NEWLINE (); + irmp_store_bit (1); + } + else // if (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX) + { // pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); // yes, store 0 + ANALYZE_NEWLINE (); + irmp_store_bit (0); + } + } + else +#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + { + ; // else do nothing + } + + irmp_pulse_time = 1; // set counter to 1, not 0 + irmp_pause_time = 0; + wait_for_start_space = 0; + } + } + else if (wait_for_space) // the data section.... + { // counting the time of darkness.... + uint8_t got_light = FALSE; + + if (irmp_input) // still dark? + { // yes... + if (irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 1) + { + if ( +#if IRMP_SUPPORT_MANCHESTER == 1 + (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) || +#endif +#if IRMP_SUPPORT_SERIAL == 1 + (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) || +#endif + (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max)) + { +#ifdef ANALYZE + if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) + { + ANALYZE_PRINTF ("stop bit detected\n"); + } +#endif + irmp_param.stop_bit = 0; + } + else + { + ANALYZE_PRINTF ("error: stop bit timing wrong, irmp_bit = %d, irmp_pulse_time = %d, pulse_0_len_min = %d, pulse_0_len_max = %d\n", + irmp_bit, irmp_pulse_time, irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max); + +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // wait for another start bit... + irmp_pulse_time = 0; + irmp_pause_time = 0; + } + } + else + { + irmp_pause_time++; // increment counter + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && // Sony has a variable number of bits: + irmp_pause_time > SIRCS_PAUSE_LEN_MAX && // minimum is 12 + irmp_bit >= 12 - 1) // pause too long? + { // yes, break and close this frame + irmp_param.complete_len = irmp_bit + 1; // set new complete length + got_light = TRUE; // this is a lie, but helps (generates stop bit) + irmp_tmp_address |= (irmp_bit - SIRCS_MINIMUM_DATA_LEN + 1) << 8; // new: store number of additional bits in upper byte of address! + irmp_param.command_end = irmp_param.command_offset + irmp_bit + 1; // correct command length + irmp_pause_time = SIRCS_PAUSE_LEN_MAX - 1; // correct pause length + } + else +#endif +#if IRMP_SUPPORT_SERIAL == 1 + // NETBOX generates no stop bit, here is the timeout condition: + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) && irmp_param.protocol == IRMP_NETBOX_PROTOCOL && + irmp_pause_time >= NETBOX_PULSE_LEN * (NETBOX_COMPLETE_DATA_LEN - irmp_bit)) + { + got_light = TRUE; // this is a lie, but helps (generates stop bit) + } + else +#endif +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && !irmp_param.stop_bit) + { + if (irmp_pause_time > IR60_TIMEOUT_LEN && irmp_bit == 6) + { + ANALYZE_PRINTF ("Switching to IR60 protocol\n"); + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + + irmp_param.protocol = IRMP_IR60_PROTOCOL; // change protocol + irmp_param.complete_len = IR60_COMPLETE_DATA_LEN; // correct complete len + irmp_param.address_offset = IR60_ADDRESS_OFFSET; + irmp_param.address_end = IR60_ADDRESS_OFFSET + IR60_ADDRESS_LEN; + irmp_param.command_offset = IR60_COMMAND_OFFSET; + irmp_param.command_end = IR60_COMMAND_OFFSET + IR60_COMMAND_LEN; + + irmp_tmp_command <<= 1; + irmp_tmp_command |= first_bit; + } + else if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN - 2) + { // special manchester decoder + irmp_param.complete_len = GRUNDIG_COMPLETE_DATA_LEN; // correct complete len + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + } + else if (irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN) + { + ANALYZE_PRINTF ("Switching to NOKIA protocol\n"); + irmp_param.protocol = IRMP_NOKIA_PROTOCOL; // change protocol + irmp_param.address_offset = NOKIA_ADDRESS_OFFSET; + irmp_param.address_end = NOKIA_ADDRESS_OFFSET + NOKIA_ADDRESS_LEN; + irmp_param.command_offset = NOKIA_COMMAND_OFFSET; + irmp_param.command_end = NOKIA_COMMAND_OFFSET + NOKIA_COMMAND_LEN; + + if (irmp_tmp_command & 0x300) + { + irmp_tmp_address = (irmp_tmp_command >> 8); + irmp_tmp_command &= 0xFF; + } + } + } + else +#endif +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RUWIDO_PROTOCOL && !irmp_param.stop_bit) + { + if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= RUWIDO_COMPLETE_DATA_LEN - 2) + { // special manchester decoder + irmp_param.complete_len = RUWIDO_COMPLETE_DATA_LEN; // correct complete len + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + } + else if (irmp_bit >= RUWIDO_COMPLETE_DATA_LEN) + { + ANALYZE_PRINTF ("Switching to SIEMENS protocol\n"); + irmp_param.protocol = IRMP_SIEMENS_PROTOCOL; // change protocol + irmp_param.address_offset = SIEMENS_ADDRESS_OFFSET; + irmp_param.address_end = SIEMENS_ADDRESS_OFFSET + SIEMENS_ADDRESS_LEN; + irmp_param.command_offset = SIEMENS_COMMAND_OFFSET; + irmp_param.command_end = SIEMENS_COMMAND_OFFSET + SIEMENS_COMMAND_LEN; + + // 76543210 + // RUWIDO: AAAAAAAAACCCCCCCp + // SIEMENS: AAAAAAAAAAACCCCCCCCCCp + irmp_tmp_address <<= 2; + irmp_tmp_address |= (irmp_tmp_command >> 6); + irmp_tmp_command &= 0x003F; + irmp_tmp_command <<= 4; + irmp_tmp_command |= last_value; + } + } + else +#endif +#if IRMP_SUPPORT_MANCHESTER == 1 + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) && + irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= irmp_param.complete_len - 2 && !irmp_param.stop_bit) + { // special manchester decoder + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + } + else +#endif // IRMP_SUPPORT_MANCHESTER == 1 + if (irmp_pause_time > IRMP_TIMEOUT_LEN) // timeout? + { // yes... + if (irmp_bit == irmp_param.complete_len - 1 && irmp_param.stop_bit == 0) + { + irmp_bit++; + } +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17)) // it was a JVC stop bit + { + ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit); + irmp_param.stop_bit = TRUE; // set flag + irmp_param.protocol = IRMP_JVC_PROTOCOL; // switch protocol + irmp_param.complete_len = irmp_bit; // patch length: 16 or 17 + irmp_tmp_command = (irmp_tmp_address >> 4); // set command: upper 12 bits are command bits + irmp_tmp_address = irmp_tmp_address & 0x000F; // lower 4 bits are address bits + irmp_start_bit_detected = 1; // tricky: don't wait for another start bit... + } +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit == 32) // it was a NEC stop bit + { + ANALYZE_PRINTF ("Switching to NEC protocol\n"); + irmp_param.stop_bit = TRUE; // set flag + irmp_param.protocol = IRMP_NEC_PROTOCOL; // switch protocol + irmp_param.complete_len = irmp_bit; // patch length: 16 or 17 + + // 0123456789ABC0123456789ABC0123456701234567 + // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc + // NEC: AAAAAAAAaaaaaaaaCCCCCCCCcccccccc + irmp_tmp_address |= (irmp_tmp_address2 & 0x0007) << 13; // fm 2012-02-13: 12 -> 13 + irmp_tmp_command = (irmp_tmp_address2 >> 3) | (irmp_tmp_command << 10); + } +#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1 +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17)) // it was a JVC stop bit + { + ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit); + irmp_param.stop_bit = TRUE; // set flag + irmp_param.protocol = IRMP_JVC_PROTOCOL; // switch protocol + irmp_param.complete_len = irmp_bit; // patch length: 16 or 17 + + // 0123456789ABC0123456789ABC0123456701234567 + // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc + // JVC: AAAACCCCCCCCCCCC + irmp_tmp_command = (irmp_tmp_address >> 4) | (irmp_tmp_address2 << 9); // set command: upper 12 bits are command bits + irmp_tmp_address = irmp_tmp_address & 0x000F; // lower 4 bits are address bits + } +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 +#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1 + else + { + ANALYZE_PRINTF ("error 2: pause %d after data bit %d too long\n", irmp_pause_time, irmp_bit); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); + +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // wait for another start bit... + irmp_pulse_time = 0; + irmp_pause_time = 0; + } + } + } + } + else + { // got light now! + got_light = TRUE; + } + + if (got_light) + { + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); + +#if IRMP_SUPPORT_MANCHESTER == 1 + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) // Manchester + { +#if 1 + if (irmp_pulse_time > irmp_param.pulse_1_len_max /* && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max */) +#else // better, but some IR-RCs use asymmetric timings :-/ + if (irmp_pulse_time > irmp_param.pulse_1_len_max && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max && + irmp_pause_time <= 2 * irmp_param.pause_1_len_max) +#endif + { +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN) // RC6 toggle bit + { + ANALYZE_PUTCHAR ('T'); + if (irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG) // RC6 mode 6A + { + irmp_store_bit (1); + last_value = 1; + } + else // RC6 mode 0 + { + irmp_store_bit (0); + last_value = 0; + } + ANALYZE_NEWLINE (); + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + { + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1'); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1 ); + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN) // RC6 toggle bit + { + ANALYZE_PUTCHAR ('T'); + irmp_store_bit (1); + + if (irmp_pause_time > 2 * irmp_param.pause_1_len_max) + { + last_value = 0; + } + else + { + last_value = 1; + } + ANALYZE_NEWLINE (); + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + { + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0'); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0 ); +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + if (! irmp_param2.protocol) +#endif + { + ANALYZE_NEWLINE (); + } + last_value = (irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0; + } + } + } + else if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max + /* && irmp_pause_time <= 2 * irmp_param.pause_1_len_max */) + { + uint8_t manchester_value; + + if (last_pause > irmp_param.pause_1_len_max && last_pause <= 2 * irmp_param.pause_1_len_max) + { + manchester_value = last_value ? 0 : 1; + last_value = manchester_value; + } + else + { + manchester_value = last_value; + } + + ANALYZE_PUTCHAR (manchester_value + '0'); + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + if (! irmp_param2.protocol) +#endif + { + ANALYZE_NEWLINE (); + } + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 1 && manchester_value == 1) // RC6 mode != 0 ??? + { + ANALYZE_PRINTF ("Switching to RC6A protocol\n"); + irmp_param.complete_len = RC6_COMPLETE_DATA_LEN_LONG; + irmp_param.address_offset = 5; + irmp_param.address_end = irmp_param.address_offset + 15; + irmp_param.command_offset = irmp_param.address_end + 1; // skip 1 system bit, changes like a toggle bit + irmp_param.command_end = irmp_param.command_offset + 16 - 1; + irmp_tmp_address = 0; + } +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + + irmp_store_bit (manchester_value); + } + else + { +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_FDC_PROTOCOL && + irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX && + ((irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX))) + { + ANALYZE_PUTCHAR ('?'); + irmp_param.protocol = 0; // switch to FDC, see below + } + else +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL && + irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX && + ((irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX))) + { + ANALYZE_PUTCHAR ('?'); + irmp_param.protocol = 0; // switch to RCCAR, see below + } + else +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + { + ANALYZE_PUTCHAR ('?'); + ANALYZE_NEWLINE (); + ANALYZE_PRINTF ("error 3 manchester: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_FDC_PROTOCOL && irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX) + { + if (irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 1 (FDC)\n"); + irmp_store_bit2 (1); + } + else if (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 0 (FDC)\n"); + irmp_store_bit2 (0); + } + + if (! irmp_param.protocol) + { + ANALYZE_PRINTF ("Switching to FDC protocol\n"); + memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER)); + irmp_param2.protocol = 0; + irmp_tmp_address = irmp_tmp_address2; + irmp_tmp_command = irmp_tmp_command2; + } + } +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL && irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX) + { + if (irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 1 (RCCAR)\n"); + irmp_store_bit2 (1); + } + else if (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 0 (RCCAR)\n"); + irmp_store_bit2 (0); + } + + if (! irmp_param.protocol) + { + ANALYZE_PRINTF ("Switching to RCCAR protocol\n"); + memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER)); + irmp_param2.protocol = 0; + irmp_tmp_address = irmp_tmp_address2; + irmp_tmp_command = irmp_tmp_command2; + } + } +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + + last_pause = irmp_pause_time; + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_MANCHESTER == 1 + +#if IRMP_SUPPORT_SERIAL == 1 + if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) + { + while (irmp_bit < irmp_param.complete_len && irmp_pulse_time > irmp_param.pulse_1_len_max) + { + ANALYZE_PUTCHAR ('1'); + irmp_store_bit (1); + + if (irmp_pulse_time >= irmp_param.pulse_1_len_min) + { + irmp_pulse_time -= irmp_param.pulse_1_len_min; + } + else + { + irmp_pulse_time = 0; + } + } + + while (irmp_bit < irmp_param.complete_len && irmp_pause_time > irmp_param.pause_1_len_max) + { + ANALYZE_PUTCHAR ('0'); + irmp_store_bit (0); + + if (irmp_pause_time >= irmp_param.pause_1_len_min) + { + irmp_pause_time -= irmp_param.pause_1_len_min; + } + else + { + irmp_pause_time = 0; + } + } + ANALYZE_NEWLINE (); + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_SERIAL == 1 + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit == 16) // Samsung: 16th bit + { + if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX && + irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("SYNC\n"); + wait_for_space = 0; + irmp_tmp_id = 0; + irmp_bit++; + } + else if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX) + { + irmp_param.protocol = IRMP_SAMSUNG32_PROTOCOL; + irmp_param.command_offset = SAMSUNG32_COMMAND_OFFSET; + irmp_param.command_end = SAMSUNG32_COMMAND_OFFSET + SAMSUNG32_COMMAND_LEN; + irmp_param.complete_len = SAMSUNG32_COMPLETE_DATA_LEN; + + if (irmp_pause_time >= SAMSUNG_1_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_1_PAUSE_LEN_MAX) + { + ANALYZE_PUTCHAR ('1'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + wait_for_space = 0; + } + else + { + ANALYZE_PUTCHAR ('0'); + ANALYZE_NEWLINE (); + irmp_store_bit (0); + wait_for_space = 0; + } + + ANALYZE_PRINTF ("Switching to SAMSUNG32 protocol\n"); + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3 Samsung: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else +#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL + +#if IRMP_SUPPORT_NEC16_PROTOCOL +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && +#else // IRMP_SUPPORT_NEC_PROTOCOL instead + if (irmp_param.protocol == IRMP_NEC_PROTOCOL && +#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1 + irmp_bit == 8 && irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("Switching to NEC16 protocol\n"); + irmp_param.protocol = IRMP_NEC16_PROTOCOL; + irmp_param.address_offset = NEC16_ADDRESS_OFFSET; + irmp_param.address_end = NEC16_ADDRESS_OFFSET + NEC16_ADDRESS_LEN; + irmp_param.command_offset = NEC16_COMMAND_OFFSET; + irmp_param.command_end = NEC16_COMMAND_OFFSET + NEC16_COMMAND_LEN; + irmp_param.complete_len = NEC16_COMPLETE_DATA_LEN; + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_NEC16_PROTOCOL + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL) + { + if (irmp_pulse_time >= BANG_OLUFSEN_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_PULSE_LEN_MAX) + { + if (irmp_bit == 1) // Bang & Olufsen: 3rd bit + { + if (irmp_pause_time >= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("3rd start bit\n"); + wait_for_space = 0; + irmp_bit++; + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3a B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else if (irmp_bit == 19) // Bang & Olufsen: trailer bit + { + if (irmp_pause_time >= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("trailer bit\n"); + wait_for_space = 0; + irmp_bit++; + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3b B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else + { + if (irmp_pause_time >= BANG_OLUFSEN_1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_1_PAUSE_LEN_MAX) + { // pulse & pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + last_value = 1; + wait_for_space = 0; + } + else if (irmp_pause_time >= BANG_OLUFSEN_0_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_0_PAUSE_LEN_MAX) + { // pulse & pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); + ANALYZE_NEWLINE (); + irmp_store_bit (0); + last_value = 0; + wait_for_space = 0; + } + else if (irmp_pause_time >= BANG_OLUFSEN_R_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_R_PAUSE_LEN_MAX) + { + ANALYZE_PUTCHAR (last_value + '0'); + ANALYZE_NEWLINE (); + irmp_store_bit (last_value); + wait_for_space = 0; + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3c B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3d B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else +#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL + + if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max && + irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max) + { // pulse & pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + wait_for_space = 0; + } + else if (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max && + irmp_pause_time >= irmp_param.pause_0_len_min && irmp_pause_time <= irmp_param.pause_0_len_max) + { // pulse & pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); + ANALYZE_NEWLINE (); + irmp_store_bit (0); + wait_for_space = 0; + } + else +#if IRMP_SUPPORT_KATHREIN_PROTOCOL + + if (irmp_param.protocol == IRMP_KATHREIN_PROTOCOL && + irmp_pulse_time >= KATHREIN_1_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_1_PULSE_LEN_MAX && + (((irmp_bit == 8 || irmp_bit == 6) && + irmp_pause_time >= KATHREIN_SYNC_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_SYNC_BIT_PAUSE_LEN_MAX) || + (irmp_bit == 12 && + irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX))) + + { + if (irmp_bit == 8) + { + irmp_bit++; + ANALYZE_PUTCHAR ('S'); + ANALYZE_NEWLINE (); + irmp_tmp_command <<= 1; + } + else + { + ANALYZE_PUTCHAR ('S'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + } + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL + { // timing incorrect! + ANALYZE_PRINTF ("error 3: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + + irmp_pulse_time = 1; // set counter to 1, not 0 + } + } + else + { // counting the pulse length ... + if (! irmp_input) // still light? + { // yes... + irmp_pulse_time++; // increment counter + } + else + { // now it's dark! + wait_for_space = 1; // let's count the time (see above) + irmp_pause_time = 1; // set pause counter to 1, not 0 + } + } + + if (irmp_start_bit_detected && irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 0) // enough bits received? + { + if (last_irmp_command == irmp_tmp_command && repetition_len < AUTO_FRAME_REPETITION_LEN) + { + repetition_frame_number++; + } + else + { + repetition_frame_number = 0; + } + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + // if SIRCS protocol and the code will be repeated within 50 ms, we will ignore 2nd and 3rd repetition frame + if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && (repetition_frame_number == 1 || repetition_frame_number == 2)) + { + ANALYZE_PRINTF ("code skipped: SIRCS auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + // if KASEIKYO protocol and the code will be repeated within 50 ms, we will ignore 2nd repetition frame + if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && repetition_frame_number == 1) + { + ANALYZE_PRINTF ("code skipped: KASEIKYO auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + // if SAMSUNG32 protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame + if (irmp_param.protocol == IRMP_SAMSUNG32_PROTOCOL && (repetition_frame_number & 0x01)) + { + ANALYZE_PRINTF ("code skipped: SAMSUNG32 auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + +#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + // if NUBERT protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame + if (irmp_param.protocol == IRMP_NUBERT_PROTOCOL && (repetition_frame_number & 0x01)) + { + ANALYZE_PRINTF ("code skipped: NUBERT auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + + { + ANALYZE_PRINTF ("%8.3fms code detected, length = %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit); + irmp_ir_detected = TRUE; + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_DENON_PROTOCOL) + { // check for repetition frame + if ((~irmp_tmp_command & 0x3FF) == last_irmp_denon_command) // command bits must be inverted + { + irmp_tmp_command = last_irmp_denon_command; // use command received before! + + irmp_protocol = irmp_param.protocol; // store protocol + irmp_address = irmp_tmp_address; // store address + irmp_command = irmp_tmp_command ; // store command + } + else + { + ANALYZE_PRINTF ("waiting for inverted command repetition\n"); + irmp_ir_detected = FALSE; + last_irmp_denon_command = irmp_tmp_command; + } + } + else +#endif // IRMP_SUPPORT_DENON_PROTOCOL + +#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && irmp_tmp_command == 0x01ff) + { // Grundig start frame? + ANALYZE_PRINTF ("Detected GRUNDIG start frame, ignoring it\n"); + irmp_ir_detected = FALSE; + } + else +#endif // IRMP_SUPPORT_GRUNDIG_PROTOCOL + +#if IRMP_SUPPORT_NOKIA_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NOKIA_PROTOCOL && irmp_tmp_address == 0x00ff && irmp_tmp_command == 0x00fe) + { // Nokia start frame? + ANALYZE_PRINTF ("Detected NOKIA start frame, ignoring it\n"); + irmp_ir_detected = FALSE; + } + else +#endif // IRMP_SUPPORT_NOKIA_PROTOCOL + { +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NEC_PROTOCOL && irmp_bit == 0) // repetition frame + { + if (repetition_len < NEC_FRAME_REPEAT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("Detected NEC repetition frame, repetition_len = %d\n", repetition_len); + irmp_tmp_address = last_irmp_address; // address is last address + irmp_tmp_command = last_irmp_command; // command is last command + irmp_flags |= IRMP_FLAG_REPETITION; + repetition_len = 0; + } + else + { + ANALYZE_PRINTF ("Detected NEC repetition frame, ignoring it: timeout occured, repetition_len = %d > %d\n", + repetition_len, NEC_FRAME_REPEAT_PAUSE_LEN_MAX); + irmp_ir_detected = FALSE; + } + } +#endif // IRMP_SUPPORT_NEC_PROTOCOL + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL) + { + uint8_t xor; + // ANALYZE_PRINTF ("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + // xor_check[0], xor_check[1], xor_check[2], xor_check[3], xor_check[4], xor_check[5]); + + xor = (xor_check[0] & 0x0F) ^ ((xor_check[0] & 0xF0) >> 4) ^ (xor_check[1] & 0x0F) ^ ((xor_check[1] & 0xF0) >> 4); + + if (xor != (xor_check[2] & 0x0F)) + { + ANALYZE_PRINTF ("error 4: wrong XOR check for customer id: 0x%1x 0x%1x\n", xor, xor_check[2] & 0x0F); + irmp_ir_detected = FALSE; + } + + xor = xor_check[2] ^ xor_check[3] ^ xor_check[4]; + + if (xor != xor_check[5]) + { + ANALYZE_PRINTF ("error 4: wrong XOR check for data bits: 0x%02x 0x%02x\n", xor, xor_check[5]); + irmp_ir_detected = FALSE; + } + + irmp_flags |= genre2; // write the genre2 bits into MSB of the flag byte + } +#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG) // RC6 mode = 6? + { + irmp_protocol = IRMP_RC6A_PROTOCOL; + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + + irmp_protocol = irmp_param.protocol; + +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_FDC_PROTOCOL) + { + if (irmp_tmp_command & 0x000F) // released key? + { + irmp_tmp_command = (irmp_tmp_command >> 4) | 0x80; // yes, set bit 7 + } + else + { + irmp_tmp_command >>= 4; // no, it's a pressed key + } + irmp_tmp_command |= (irmp_tmp_address << 2) & 0x0F00; // 000000CCCCAAAAAA -> 0000CCCC00000000 + irmp_tmp_address &= 0x003F; + } +#endif + + irmp_address = irmp_tmp_address; // store address +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NEC_PROTOCOL) + { + last_irmp_address = irmp_tmp_address; // store as last address, too + } +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC5_PROTOCOL) + { + irmp_tmp_command |= rc5_cmd_bit6; // store bit 6 + } +#endif + irmp_command = irmp_tmp_command; // store command + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + irmp_id = irmp_tmp_id; +#endif + } + } + + if (irmp_ir_detected) + { + if (last_irmp_command == irmp_tmp_command && + last_irmp_address == irmp_tmp_address && + repetition_len < IRMP_KEY_REPETITION_LEN) + { + irmp_flags |= IRMP_FLAG_REPETITION; + } + + last_irmp_address = irmp_tmp_address; // store as last address, too + last_irmp_command = irmp_tmp_command; // store as last command, too + + repetition_len = 0; + } + else + { + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); + } + +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // and wait for next start bit + irmp_tmp_command = 0; + irmp_pulse_time = 0; + irmp_pause_time = 0; + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL) // the stop bit of JVC frame is also start bit of next frame + { // set pulse time here! + irmp_pulse_time = ((uint8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME)); + } +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + } + } + } + return (irmp_ir_detected); +} + +#ifdef ANALYZE + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * main functions - for Unix/Linux + Windows only! + * + * AVR: see main.c! + * + * Compile it under linux with: + * cc irmp.c -o irmp + * + * usage: ./irmp [-v|-s|-a|-l|-p] < file + * + * options: + * -v verbose + * -s silent + * -a analyze + * -l list pulse/pauses + * -p print timings + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#ifndef IRMP_EMBED +static void +print_timings (void) +{ + printf ("IRMP_TIMEOUT_LEN: %d [%d byte(s)]\n", IRMP_TIMEOUT_LEN, sizeof (PAUSE_LEN)); + printf ("IRMP_KEY_REPETITION_LEN %d\n", IRMP_KEY_REPETITION_LEN); + puts (""); + printf ("PROTOCOL S S-PULSE S-PAUSE PULSE-0 PAUSE-0 PULSE-1 PAUSE-1\n"); + printf ("====================================================================================\n"); + printf ("SIRCS 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + SIRCS_START_BIT_PULSE_LEN_MIN, SIRCS_START_BIT_PULSE_LEN_MAX, SIRCS_START_BIT_PAUSE_LEN_MIN, SIRCS_START_BIT_PAUSE_LEN_MAX, + SIRCS_0_PULSE_LEN_MIN, SIRCS_0_PULSE_LEN_MAX, SIRCS_PAUSE_LEN_MIN, SIRCS_PAUSE_LEN_MAX, + SIRCS_1_PULSE_LEN_MIN, SIRCS_1_PULSE_LEN_MAX, SIRCS_PAUSE_LEN_MIN, SIRCS_PAUSE_LEN_MAX); + + printf ("NEC 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_1_PAUSE_LEN_MIN, NEC_1_PAUSE_LEN_MAX); + + printf ("NEC (rep) 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_1_PAUSE_LEN_MIN, NEC_1_PAUSE_LEN_MAX); + + printf ("SAMSUNG 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + SAMSUNG_START_BIT_PULSE_LEN_MIN, SAMSUNG_START_BIT_PULSE_LEN_MAX, SAMSUNG_START_BIT_PAUSE_LEN_MIN, SAMSUNG_START_BIT_PAUSE_LEN_MAX, + SAMSUNG_PULSE_LEN_MIN, SAMSUNG_PULSE_LEN_MAX, SAMSUNG_0_PAUSE_LEN_MIN, SAMSUNG_0_PAUSE_LEN_MAX, + SAMSUNG_PULSE_LEN_MIN, SAMSUNG_PULSE_LEN_MAX, SAMSUNG_1_PAUSE_LEN_MIN, SAMSUNG_1_PAUSE_LEN_MAX); + + printf ("MATSUSHITA 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + MATSUSHITA_START_BIT_PULSE_LEN_MIN, MATSUSHITA_START_BIT_PULSE_LEN_MAX, MATSUSHITA_START_BIT_PAUSE_LEN_MIN, MATSUSHITA_START_BIT_PAUSE_LEN_MAX, + MATSUSHITA_PULSE_LEN_MIN, MATSUSHITA_PULSE_LEN_MAX, MATSUSHITA_0_PAUSE_LEN_MIN, MATSUSHITA_0_PAUSE_LEN_MAX, + MATSUSHITA_PULSE_LEN_MIN, MATSUSHITA_PULSE_LEN_MAX, MATSUSHITA_1_PAUSE_LEN_MIN, MATSUSHITA_1_PAUSE_LEN_MAX); + + printf ("KASEIKYO 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + KASEIKYO_START_BIT_PULSE_LEN_MIN, KASEIKYO_START_BIT_PULSE_LEN_MAX, KASEIKYO_START_BIT_PAUSE_LEN_MIN, KASEIKYO_START_BIT_PAUSE_LEN_MAX, + KASEIKYO_PULSE_LEN_MIN, KASEIKYO_PULSE_LEN_MAX, KASEIKYO_0_PAUSE_LEN_MIN, KASEIKYO_0_PAUSE_LEN_MAX, + KASEIKYO_PULSE_LEN_MIN, KASEIKYO_PULSE_LEN_MAX, KASEIKYO_1_PAUSE_LEN_MIN, KASEIKYO_1_PAUSE_LEN_MAX); + + printf ("RECS80 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RECS80_START_BIT_PULSE_LEN_MIN, RECS80_START_BIT_PULSE_LEN_MAX, RECS80_START_BIT_PAUSE_LEN_MIN, RECS80_START_BIT_PAUSE_LEN_MAX, + RECS80_PULSE_LEN_MIN, RECS80_PULSE_LEN_MAX, RECS80_0_PAUSE_LEN_MIN, RECS80_0_PAUSE_LEN_MAX, + RECS80_PULSE_LEN_MIN, RECS80_PULSE_LEN_MAX, RECS80_1_PAUSE_LEN_MIN, RECS80_1_PAUSE_LEN_MAX); + + printf ("RC5 1 %3d - %3d %3d - %3d %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + RC5_BIT_LEN_MIN, RC5_BIT_LEN_MAX); + + printf ("DENON 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, DENON_0_PAUSE_LEN_MIN, DENON_0_PAUSE_LEN_MAX, + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, DENON_1_PAUSE_LEN_MIN, DENON_1_PAUSE_LEN_MAX); + + printf ("THOMSON 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, THOMSON_0_PAUSE_LEN_MIN, THOMSON_0_PAUSE_LEN_MAX, + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, THOMSON_1_PAUSE_LEN_MIN, THOMSON_1_PAUSE_LEN_MAX); + + printf ("RC6 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RC6_START_BIT_PULSE_LEN_MIN, RC6_START_BIT_PULSE_LEN_MAX, RC6_START_BIT_PAUSE_LEN_MIN, RC6_START_BIT_PAUSE_LEN_MAX, + RC6_BIT_PULSE_LEN_MIN, RC6_BIT_PULSE_LEN_MAX, RC6_BIT_PAUSE_LEN_MIN, RC6_BIT_PAUSE_LEN_MAX); + + printf ("RECS80EXT 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RECS80EXT_START_BIT_PULSE_LEN_MIN, RECS80EXT_START_BIT_PULSE_LEN_MAX, RECS80EXT_START_BIT_PAUSE_LEN_MIN, RECS80EXT_START_BIT_PAUSE_LEN_MAX, + RECS80EXT_PULSE_LEN_MIN, RECS80EXT_PULSE_LEN_MAX, RECS80EXT_0_PAUSE_LEN_MIN, RECS80EXT_0_PAUSE_LEN_MAX, + RECS80EXT_PULSE_LEN_MIN, RECS80EXT_PULSE_LEN_MAX, RECS80EXT_1_PAUSE_LEN_MIN, RECS80EXT_1_PAUSE_LEN_MAX); + + printf ("NUBERT 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NUBERT_START_BIT_PULSE_LEN_MIN, NUBERT_START_BIT_PULSE_LEN_MAX, NUBERT_START_BIT_PAUSE_LEN_MIN, NUBERT_START_BIT_PAUSE_LEN_MAX, + NUBERT_0_PULSE_LEN_MIN, NUBERT_0_PULSE_LEN_MAX, NUBERT_0_PAUSE_LEN_MIN, NUBERT_0_PAUSE_LEN_MAX, + NUBERT_1_PULSE_LEN_MIN, NUBERT_1_PULSE_LEN_MAX, NUBERT_1_PAUSE_LEN_MIN, NUBERT_1_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 1 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 2 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 3 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 4 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN - %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_PULSE_LEN_MIN, BANG_OLUFSEN_PULSE_LEN_MAX, BANG_OLUFSEN_0_PAUSE_LEN_MIN, BANG_OLUFSEN_0_PAUSE_LEN_MAX, + BANG_OLUFSEN_PULSE_LEN_MIN, BANG_OLUFSEN_PULSE_LEN_MAX, BANG_OLUFSEN_1_PAUSE_LEN_MIN, BANG_OLUFSEN_1_PAUSE_LEN_MAX); + + printf ("GRUNDIG/NOKIA 1 %3d - %3d %3d - %3d %3d - %3d\n", + GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX, + GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN, GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX, + GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_BIT_LEN_MAX); + + printf ("SIEMENS/RUWIDO 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX, + SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX, + SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, + SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX); + + printf ("GRUNDIG2 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + GRUNDIG2_START_BIT_PULSE_LEN_MIN, GRUNDIG2_START_BIT_PULSE_LEN_MAX, + GRUNDIG2_START_BIT_PAUSE_LEN_MIN, GRUNDIG2_START_BIT_PAUSE_LEN_MAX, + GRUNDIG2_BIT_PULSE_LEN_MIN, GRUNDIG2_BIT_PULSE_LEN_MAX, + GRUNDIG2_BIT_PAUSE_LEN_MIN, GRUNDIG2_BIT_PAUSE_LEN_MAX, + 2 * GRUNDIG2_BIT_PULSE_LEN_MIN, 2 * GRUNDIG2_BIT_PULSE_LEN_MAX, + 2 * GRUNDIG2_BIT_PAUSE_LEN_MIN, 2 * GRUNDIG2_BIT_PAUSE_LEN_MAX); + + printf ("FDC 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX, + FDC_PULSE_LEN_MIN, FDC_PULSE_LEN_MAX, FDC_0_PAUSE_LEN_MIN, FDC_0_PAUSE_LEN_MAX, + FDC_PULSE_LEN_MIN, FDC_PULSE_LEN_MAX, FDC_1_PAUSE_LEN_MIN, FDC_1_PAUSE_LEN_MAX); + + printf ("RCCAR 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX, RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX, + RCCAR_PULSE_LEN_MIN, RCCAR_PULSE_LEN_MAX, RCCAR_0_PAUSE_LEN_MIN, RCCAR_0_PAUSE_LEN_MAX, + RCCAR_PULSE_LEN_MIN, RCCAR_PULSE_LEN_MAX, RCCAR_1_PAUSE_LEN_MIN, RCCAR_1_PAUSE_LEN_MAX); + + printf ("NIKON 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NIKON_START_BIT_PULSE_LEN_MIN, NIKON_START_BIT_PULSE_LEN_MAX, NIKON_START_BIT_PAUSE_LEN_MIN, NIKON_START_BIT_PAUSE_LEN_MAX, + NIKON_PULSE_LEN_MIN, NIKON_PULSE_LEN_MAX, NIKON_0_PAUSE_LEN_MIN, NIKON_0_PAUSE_LEN_MAX, + NIKON_PULSE_LEN_MIN, NIKON_PULSE_LEN_MAX, NIKON_1_PAUSE_LEN_MIN, NIKON_1_PAUSE_LEN_MAX); + + printf ("LEGO 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + LEGO_START_BIT_PULSE_LEN_MIN, LEGO_START_BIT_PULSE_LEN_MAX, LEGO_START_BIT_PAUSE_LEN_MIN, LEGO_START_BIT_PAUSE_LEN_MAX, + LEGO_PULSE_LEN_MIN, LEGO_PULSE_LEN_MAX, LEGO_0_PAUSE_LEN_MIN, LEGO_0_PAUSE_LEN_MAX, + LEGO_PULSE_LEN_MIN, LEGO_PULSE_LEN_MAX, LEGO_1_PAUSE_LEN_MIN, LEGO_1_PAUSE_LEN_MAX); + +} + +void +print_spectrum (char * text, int * buf, int is_pulse) +{ + int i; + int j; + int min; + int max; + int max_value = 0; + int value; + int sum = 0; + int counter = 0; + double average = 0; + double tolerance; + + puts ("-------------------------------------------------------------------------------"); + printf ("%s:\n", text); + + for (i = 0; i < 256; i++) + { + if (buf[i] > max_value) + { + max_value = buf[i]; + } + } + + for (i = 1; i < 100; i++) + { + if (buf[i] > 0) + { + printf ("%3d ", i); + value = (buf[i] * 60) / max_value; + + for (j = 0; j < value; j++) + { + putchar ('o'); + } + printf (" %d\n", buf[i]); + + sum += i * buf[i]; + counter += buf[i]; + } + else + { + max = i - 1; + + if (counter > 0) + { + average = (float) sum / (float) counter; + + if (is_pulse) + { + printf ("pulse "); + } + else + { + printf ("pause "); + } + + printf ("avg: %4.1f=%6.1f us, ", average, (1000000. * average) / (float) F_INTERRUPTS); + printf ("min: %2d=%6.1f us, ", min, (1000000. * min) / (float) F_INTERRUPTS); + printf ("max: %2d=%6.1f us, ", max, (1000000. * max) / (float) F_INTERRUPTS); + + tolerance = (max - average); + + if (average - min > tolerance) + { + tolerance = average - min; + } + + tolerance = tolerance * 100 / average; + printf ("tol: %4.1f%%\n", tolerance); + } + + counter = 0; + sum = 0; + min = i + 1; + } + } +} +#endif + +#define STATE_LEFT_SHIFT 0x01 +#define STATE_RIGHT_SHIFT 0x02 +#define STATE_LEFT_CTRL 0x04 +#define STATE_LEFT_ALT 0x08 +#define STATE_RIGHT_ALT 0x10 + +#define KEY_ESCAPE 0x1B // keycode = 0x006e +#define KEY_MENUE 0x80 // keycode = 0x0070 +#define KEY_BACK 0x81 // keycode = 0x0071 +#define KEY_FORWARD 0x82 // keycode = 0x0072 +#define KEY_ADDRESS 0x83 // keycode = 0x0073 +#define KEY_WINDOW 0x84 // keycode = 0x0074 +#define KEY_1ST_PAGE 0x85 // keycode = 0x0075 +#define KEY_STOP 0x86 // keycode = 0x0076 +#define KEY_MAIL 0x87 // keycode = 0x0077 +#define KEY_FAVORITES 0x88 // keycode = 0x0078 +#define KEY_NEW_PAGE 0x89 // keycode = 0x0079 +#define KEY_SETUP 0x8A // keycode = 0x007a +#define KEY_FONT 0x8B // keycode = 0x007b +#define KEY_PRINT 0x8C // keycode = 0x007c +#define KEY_ON_OFF 0x8E // keycode = 0x007c + +#define KEY_INSERT 0x90 // keycode = 0x004b +#define KEY_DELETE 0x91 // keycode = 0x004c +#define KEY_LEFT 0x92 // keycode = 0x004f +#define KEY_HOME 0x93 // keycode = 0x0050 +#define KEY_END 0x94 // keycode = 0x0051 +#define KEY_UP 0x95 // keycode = 0x0053 +#define KEY_DOWN 0x96 // keycode = 0x0054 +#define KEY_PAGE_UP 0x97 // keycode = 0x0055 +#define KEY_PAGE_DOWN 0x98 // keycode = 0x0056 +#define KEY_RIGHT 0x99 // keycode = 0x0059 +#define KEY_MOUSE_1 0x9E // keycode = 0x0400 +#define KEY_MOUSE_2 0x9F // keycode = 0x0800 + +#ifndef LIRC_IRMP +static uint8_t +get_fdc_key (uint16_t cmd) +{ + static uint8_t key_table[128] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, '^', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'ß', '´', 0, '\b', + '\t','q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 'ü', '+', 0, 0, 'a', + 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ö', 'ä', '#', '\r', 0, '<', 'y', 'x', + 'c', 'v', 'b', 'n', 'm', ',', '.', '-', 0, 0, 0, 0, 0, ' ', 0, 0, + + 0, '°', '!', '"', '§', '$', '%', '&', '/', '(', ')', '=', '?', '`', 0, '\b', + '\t','Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P', 'Ü', '*', 0, 0, 'A', + 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Ö', 'Ä', '\'','\r', 0, '>', 'Y', 'X', + 'C', 'V', 'B', 'N', 'M', ';', ':', '_', 0, 0, 0, 0, 0, ' ', 0, 0 + }; + static uint8_t state; + + uint8_t key = 0; + + switch (cmd) + { + case 0x002C: state |= STATE_LEFT_SHIFT; break; // pressed left shift + case 0x00AC: state &= ~STATE_LEFT_SHIFT; break; // released left shift + case 0x0039: state |= STATE_RIGHT_SHIFT; break; // pressed right shift + case 0x00B9: state &= ~STATE_RIGHT_SHIFT; break; // released right shift + case 0x003A: state |= STATE_LEFT_CTRL; break; // pressed left ctrl + case 0x00BA: state &= ~STATE_LEFT_CTRL; break; // released left ctrl + case 0x003C: state |= STATE_LEFT_ALT; break; // pressed left alt + case 0x00BC: state &= ~STATE_LEFT_ALT; break; // released left alt + case 0x003E: state |= STATE_RIGHT_ALT; break; // pressed left alt + case 0x00BE: state &= ~STATE_RIGHT_ALT; break; // released left alt + + case 0x006e: key = KEY_ESCAPE; break; + case 0x004b: key = KEY_INSERT; break; + case 0x004c: key = KEY_DELETE; break; + case 0x004f: key = KEY_LEFT; break; + case 0x0050: key = KEY_HOME; break; + case 0x0051: key = KEY_END; break; + case 0x0053: key = KEY_UP; break; + case 0x0054: key = KEY_DOWN; break; + case 0x0055: key = KEY_PAGE_UP; break; + case 0x0056: key = KEY_PAGE_DOWN; break; + case 0x0059: key = KEY_RIGHT; break; + case 0x0400: key = KEY_MOUSE_1; break; + case 0x0800: key = KEY_MOUSE_2; break; + + default: + { + if (!(cmd & 0x80)) // pressed key + { + if (cmd >= 0x70 && cmd <= 0x7F) // function keys + { + key = cmd + 0x10; // 7x -> 8x + } + else if (cmd < 64) // key listed in key_table + { + if (state & (STATE_LEFT_ALT | STATE_RIGHT_ALT)) + { + switch (cmd) + { + case 0x0003: key = '²'; break; + case 0x0008: key = '{'; break; + case 0x0009: key = '['; break; + case 0x000A: key = ']'; break; + case 0x000B: key = '}'; break; + case 0x000C: key = '\\'; break; + case 0x001C: key = '~'; break; + case 0x002D: key = '|'; break; + case 0x0034: key = 0xB5; break; // Mu + } + } + else if (state & (STATE_LEFT_CTRL)) + { + if (key_table[cmd] >= 'a' && key_table[cmd] <= 'z') + { + key = key_table[cmd] - 'a' + 1; + } + else + { + key = key_table[cmd]; + } + } + else + { + int idx = cmd + ((state & (STATE_LEFT_SHIFT | STATE_RIGHT_SHIFT)) ? 64 : 0); + + if (key_table[idx]) + { + key = key_table[idx]; + } + } + } + } + break; + } + } + + return (key); +} + +static int analyze = FALSE; +static int list = FALSE; +static IRMP_DATA irmp_data; + +static void +next_tick (void) +{ + if (! analyze && ! list) + { + (void) irmp_ISR (); + + if (irmp_get_data (&irmp_data)) + { + uint8_t key; + + ANALYZE_ONLY_NORMAL_PUTCHAR (' '); + + if (verbose) + { + printf ("%8.3fms ", (double) (time_counter * 1000) / F_INTERRUPTS); + } + + if (irmp_data.protocol == IRMP_FDC_PROTOCOL && (key = get_fdc_key (irmp_data.command)) != 0) + { + if ((key >= 0x20 && key < 0x7F) || key >= 0xA0) + { + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x, asc = 0x%02x, key = '%c'\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags, key, key); + } + else if (key == '\r' || key == '\t' || key == KEY_ESCAPE || (key >= 0x80 && key <= 0x9F)) // function keys + { + char * p = (char *) NULL; + + switch (key) + { + case '\t' : p = "TAB"; break; + case '\r' : p = "CR"; break; + case KEY_ESCAPE : p = "ESCAPE"; break; + case KEY_MENUE : p = "MENUE"; break; + case KEY_BACK : p = "BACK"; break; + case KEY_FORWARD : p = "FORWARD"; break; + case KEY_ADDRESS : p = "ADDRESS"; break; + case KEY_WINDOW : p = "WINDOW"; break; + case KEY_1ST_PAGE : p = "1ST_PAGE"; break; + case KEY_STOP : p = "STOP"; break; + case KEY_MAIL : p = "MAIL"; break; + case KEY_FAVORITES : p = "FAVORITES"; break; + case KEY_NEW_PAGE : p = "NEW_PAGE"; break; + case KEY_SETUP : p = "SETUP"; break; + case KEY_FONT : p = "FONT"; break; + case KEY_PRINT : p = "PRINT"; break; + case KEY_ON_OFF : p = "ON_OFF"; break; + + case KEY_INSERT : p = "INSERT"; break; + case KEY_DELETE : p = "DELETE"; break; + case KEY_LEFT : p = "LEFT"; break; + case KEY_HOME : p = "HOME"; break; + case KEY_END : p = "END"; break; + case KEY_UP : p = "UP"; break; + case KEY_DOWN : p = "DOWN"; break; + case KEY_PAGE_UP : p = "PAGE_UP"; break; + case KEY_PAGE_DOWN : p = "PAGE_DOWN"; break; + case KEY_RIGHT : p = "RIGHT"; break; + case KEY_MOUSE_1 : p = "KEY_MOUSE_1"; break; + case KEY_MOUSE_2 : p = "KEY_MOUSE_2"; break; + default : p = ""; break; + } + + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x, asc = 0x%02x, key = %s\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags, key, p); + } + else + { + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x, asc = 0x%02x\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags, key); + } + } + else + { + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags); + } + } + } +} +#endif + +#ifndef LIRC_IRMP +int +main (int argc, char ** argv) +{ + int i; + int ch; + int last_ch = 0; + int pulse = 0; + int pause = 0; + + int start_pulses[256]; + int start_pauses[256]; + int pulses[256]; + int pauses[256]; + + int first_pulse = TRUE; + int first_pause = TRUE; + + if (argc == 2) + { + if (! strcmp (argv[1], "-v")) + { + verbose = TRUE; + } + else if (! strcmp (argv[1], "-l")) + { + list = TRUE; + } + else if (! strcmp (argv[1], "-a")) + { + analyze = TRUE; + } + else if (! strcmp (argv[1], "-s")) + { + silent = TRUE; + } + else if (! strcmp (argv[1], "-p")) + { + print_timings (); + return (0); + } + } + + for (i = 0; i < 256; i++) + { + start_pulses[i] = 0; + start_pauses[i] = 0; + pulses[i] = 0; + pauses[i] = 0; + } + + IRMP_PIN = 0xFF; + + while ((ch = getchar ()) != EOF) + { + if (ch == '_' || ch == '0') + { + if (last_ch != ch) + { + if (pause > 0) + { + if (list) + { + printf ("pause: %d\n", pause); + } + + if (analyze) + { + if (first_pause) + { + if (pause < 256) + { + start_pauses[pause]++; + } + first_pause = FALSE; + } + else + { + if (pause < 256) + { + pauses[pause]++; + } + } + } + } + pause = 0; + } + pulse++; + IRMP_PIN = 0x00; + } + else if (ch == 0xaf || ch == '-' || ch == '1') + { + if (last_ch != ch) + { + if (list) + { + printf ("pulse: %d ", pulse); + } + + if (analyze) + { + if (first_pulse) + { + if (pulse < 256) + { + start_pulses[pulse]++; + } + first_pulse = FALSE; + } + else + { + if (pulse < 256) + { + pulses[pulse]++; + } + } + } + pulse = 0; + } + + pause++; + IRMP_PIN = 0xff; + } + else if (ch == '\n') + { + IRMP_PIN = 0xff; + + if (list && pause > 0) + { + printf ("pause: %d\n", pause); + } + pause = 0; + + if (! analyze) + { + for (i = 0; i < (int) ((8000.0 * F_INTERRUPTS) / 10000); i++) // newline: long pause of 800 msec + { + next_tick (); + } + } + first_pulse = TRUE; + first_pause = TRUE; + } + else if (ch == '#') + { + if (analyze) + { + while ((ch = getchar()) != '\n' && ch != EOF) + { + ; + } + } + else + { + puts ("-------------------------------------------------------------------"); + putchar (ch); + + while ((ch = getchar()) != '\n' && ch != EOF) + { + if (ch != '\r') // ignore CR in DOS/Windows files + { + putchar (ch); + } + } + putchar ('\n'); + } + + } + + last_ch = ch; + + next_tick (); + } + + if (analyze) + { + print_spectrum ("START PULSES", start_pulses, TRUE); + print_spectrum ("START PAUSES", start_pauses, FALSE); + print_spectrum ("PULSES", pulses, TRUE); + print_spectrum ("PAUSES", pauses, FALSE); + puts ("-------------------------------------------------------------------------------"); + } + return 0; +} +#else +#ifndef IRMP_EMBED +/* 50 ms. This should be longer than the longest light pulse */ +#define POLL_MS (50 * 1000) +#define LIRC_PULSE 0x01000000 +#define LIRC_PULSE_MASK 0x00FFFFFF + +int main (int argc, char ** argv) +{ + int fd; + int pulse; + int last_pulse = 1; + uint32_t lircdata; /* lirc_t to be correct... */ + unsigned int count = 0; /* how many timeouts? */ + IRMP_DATA d; + + silent = TRUE; + + if (argc == 2) + { + if (! strcmp (argv[1], "-v")) + { + verbose = TRUE; + silent = FALSE; + } + else if (! strcmp (argv[1], "-p")) + { + print_timings (); + return (0); + } + } + + IRMP_PIN = 0xFF; + fd = open("/dev/lirc", O_RDONLY); + if (fd < 0) + { + perror ("open /dev/lirc"); + return 1; + } + /* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */ + + while(1) + { + fd_set fds; + struct timeval tv; + int ret; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = 0; + tv.tv_usec = POLL_MS; + /* any singal can interrupt select. we rely on the linux-only feature + * that the timeout is automatcally recalculated in this case! */ + do { + ret = select(fd + 1, &fds, NULL, NULL, &tv); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* errno != EINTR... */ + perror("lirmp: select"); + break; + } + + if (ret == 0) + { + count++; + lircdata = POLL_MS; /* timeout */ + pulse = !last_pulse; /* lirc sends data on signal change */ + } + else + { + if (read(fd, &lircdata, sizeof(lircdata)) != sizeof(lircdata)) + { + perror("read"); + break; + } + pulse = (lircdata & LIRC_PULSE); /* we got light... */ + last_pulse = pulse; + lircdata &= LIRC_PULSE_MASK; /* how long the pulse was in microseconds */ + } + + if (ret && count) + { + if (count * POLL_MS > lircdata) + lircdata = 0; + else + lircdata -= count * POLL_MS; + count = 0; + } + //printf("lircdata: ret:%d c:%d %d\n", ret, ch - '0', lircdata); + lircdata /= (1000000 / F_INTERRUPTS); + + if (pulse) + IRMP_PIN = 0x00; + else + IRMP_PIN = 0xff; + + do { + (void) irmp_ISR (); + if (irmp_get_data (&d)) + { + printf("protocol: %2d address: 0x%04x command: 0x%04x flags: %d\n", + d.protocol, d.address, d.command, d.flags); + + /* do something else here... */ + + /* todo: do we need to complete the loop if we already + * detected the singal in this pulse? */ + } + } while (lircdata-- > 0); + } + return 0; +} +#endif // IRMP_EMBED +#endif // LIRC_IRMP +#endif // ANALYZE diff --git a/libarmbox/irmp.h b/libarmbox/irmp.h new file mode 100644 index 0000000..1e3852b --- /dev/null +++ b/libarmbox/irmp.h @@ -0,0 +1,528 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * irmp.h + * + * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de + * + * $Id: irmp.h,v 1.70 2012/02/21 08:41:46 fm Exp $ + * + * ATMEGA88 @ 8 MHz + * + * 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; either version 2 of the License, or + * (at your option) any later version. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#ifndef _WC_IRMP_H_ +#define _WC_IRMP_H_ + +#if defined(__18CXX) // Microchip C18 declaration of missing typedef +typedef unsigned char uint8_t; +typedef unsigned int uint16_t; +#endif //Microchip C18 + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * timing constants: + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +// fm 22.09.2011: may not be more than 16000L, otherwise some JVC codes will not be accepted +#define IRMP_TIMEOUT_TIME 15500.0e-6 // timeout after 15.5 ms darkness +#define IRMP_TIMEOUT_TIME_MS 15500L // timeout after 15.5 ms darkness + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 +#define IRMP_TIMEOUT_NIKON_TIME 29500.0e-6 // 2nd timeout after 29.5 ms darkness (only for NIKON!) +#define IRMP_TIMEOUT_NIKON_TIME_MS 29500L // 2nd timeout after 29.5 ms darkness +typedef uint16_t PAUSE_LEN; +#define IRMP_TIMEOUT_NIKON_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_NIKON_TIME + 0.5) +#else +#if (F_INTERRUPTS * IRMP_TIMEOUT_TIME_MS) / 1000000 >= 254 +typedef uint16_t PAUSE_LEN; +#else +typedef uint8_t PAUSE_LEN; +#endif +#endif + +#define IRMP_TIMEOUT_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_TIME + 0.5) + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * IR protocols + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#define IRMP_SIRCS_PROTOCOL 1 // Sony +#define IRMP_NEC_PROTOCOL 2 // NEC, Pioneer, JVC, Toshiba, NoName etc. +#define IRMP_SAMSUNG_PROTOCOL 3 // Samsung +#define IRMP_MATSUSHITA_PROTOCOL 4 // Matsushita +#define IRMP_KASEIKYO_PROTOCOL 5 // Kaseikyo (Panasonic etc) +#define IRMP_RECS80_PROTOCOL 6 // Philips, Thomson, Nordmende, Telefunken, Saba +#define IRMP_RC5_PROTOCOL 7 // Philips etc +#define IRMP_DENON_PROTOCOL 8 // Denon, Sharp +#define IRMP_RC6_PROTOCOL 9 // Philips etc +#define IRMP_SAMSUNG32_PROTOCOL 10 // Samsung32: no sync pulse at bit 16, length 32 instead of 37 +#define IRMP_APPLE_PROTOCOL 11 // Apple, very similar to NEC +#define IRMP_RECS80EXT_PROTOCOL 12 // Philips, Technisat, Thomson, Nordmende, Telefunken, Saba +#define IRMP_NUBERT_PROTOCOL 13 // Nubert +#define IRMP_BANG_OLUFSEN_PROTOCOL 14 // Bang & Olufsen +#define IRMP_GRUNDIG_PROTOCOL 15 // Grundig +#define IRMP_NOKIA_PROTOCOL 16 // Nokia +#define IRMP_SIEMENS_PROTOCOL 17 // Siemens, e.g. Gigaset +#define IRMP_FDC_PROTOCOL 18 // FDC keyboard +#define IRMP_RCCAR_PROTOCOL 19 // RC Car +#define IRMP_JVC_PROTOCOL 20 // JVC (NEC with 16 bits) +#define IRMP_RC6A_PROTOCOL 21 // RC6A, e.g. Kathrein, XBOX +#define IRMP_NIKON_PROTOCOL 22 // Nikon +#define IRMP_RUWIDO_PROTOCOL 23 // Ruwido, e.g. T-Home Mediareceiver +#define IRMP_IR60_PROTOCOL 24 // IR60 (SAB2008) +#define IRMP_KATHREIN_PROTOCOL 25 // Kathrein +#define IRMP_NETBOX_PROTOCOL 26 // Netbox keyboard (bitserial) +#define IRMP_NEC16_PROTOCOL 27 // NEC with 16 bits (incl. sync) +#define IRMP_NEC42_PROTOCOL 28 // NEC with 42 bits +#define IRMP_LEGO_PROTOCOL 29 // LEGO Power Functions RC +#define IRMP_THOMSON_PROTOCOL 30 // Thomson +#define IRMP_GRUNDIG2_PROTOCOL 31 // Grundig, e.g. TP400 + +#define IRMP_N_PROTOCOLS 31 // number of supported protocols + +// some flags of struct IRMP_PARAMETER: +#define IRMP_PARAM_FLAG_IS_MANCHESTER 0x01 +#define IRMP_PARAM_FLAG_1ST_PULSE_IS_1 0x02 +#define IRMP_PARAM_FLAG_IS_SERIAL 0x04 + +#define SIRCS_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse +#define SIRCS_START_BIT_PAUSE_TIME 600.0e-6 // 600 usec pause +#define SIRCS_1_PULSE_TIME 1200.0e-6 // 1200 usec pulse +#define SIRCS_0_PULSE_TIME 600.0e-6 // 600 usec pulse +#define SIRCS_PAUSE_TIME 600.0e-6 // 600 usec pause +#define SIRCS_FRAMES 3 // SIRCS sends each frame 3 times +#define SIRCS_AUTO_REPETITION_PAUSE_TIME 25.0e-3 // auto repetition after 25ms +#define SIRCS_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms +#define SIRCS_ADDRESS_OFFSET 15 // skip 15 bits +#define SIRCS_ADDRESS_LEN 5 // read up to 5 address bits +#define SIRCS_COMMAND_OFFSET 0 // skip 0 bits +#define SIRCS_COMMAND_LEN 15 // read 12-15 command bits +#define SIRCS_MINIMUM_DATA_LEN 12 // minimum data length +#define SIRCS_COMPLETE_DATA_LEN 20 // complete length - may be up to 20 +#define SIRCS_STOP_BIT 0 // has no stop bit +#define SIRCS_LSB 1 // LSB...MSB +#define SIRCS_FLAGS 0 // flags + +#define NEC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse +#define NEC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define NEC_REPEAT_START_BIT_PAUSE_TIME 2250.0e-6 // 2250 usec pause +#define NEC_PULSE_TIME 560.0e-6 // 560 usec pulse +#define NEC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause +#define NEC_0_PAUSE_TIME 560.0e-6 // 560 usec pause +#define NEC_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms +#define NEC_ADDRESS_OFFSET 0 // skip 0 bits +#define NEC_ADDRESS_LEN 16 // read 16 address bits +#define NEC_COMMAND_OFFSET 16 // skip 16 bits (8 address + 8 /address) +#define NEC_COMMAND_LEN 16 // read 16 bits (8 command + 8 /command) +#define NEC_COMPLETE_DATA_LEN 32 // complete length +#define NEC_STOP_BIT 1 // has stop bit +#define NEC_LSB 1 // LSB...MSB +#define NEC_FLAGS 0 // flags + +#define NEC42_ADDRESS_OFFSET 0 // skip 0 bits +#define NEC42_ADDRESS_LEN 13 // read 13 address bits +#define NEC42_COMMAND_OFFSET 26 // skip 26 bits (2 x 13 address bits) +#define NEC42_COMMAND_LEN 8 // read 8 command bits +#define NEC42_COMPLETE_DATA_LEN 42 // complete length (2 x 13 + 2 x 8) + +#define NEC16_ADDRESS_OFFSET 0 // skip 0 bits +#define NEC16_ADDRESS_LEN 8 // read 8 address bits +#define NEC16_COMMAND_OFFSET 8 // skip 8 bits (8 address) +#define NEC16_COMMAND_LEN 8 // read 8 bits (8 command) +#define NEC16_COMPLETE_DATA_LEN 16 // complete length + +#define SAMSUNG_START_BIT_PULSE_TIME 4500.0e-6 // 4500 usec pulse +#define SAMSUNG_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define SAMSUNG_PULSE_TIME 550.0e-6 // 550 usec pulse +#define SAMSUNG_1_PAUSE_TIME 1650.0e-6 // 1650 usec pause +#define SAMSUNG_0_PAUSE_TIME 550.0e-6 // 550 usec pause + +#define SAMSUNG_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms +#define SAMSUNG_ADDRESS_OFFSET 0 // skip 0 bits +#define SAMSUNG_ADDRESS_LEN 16 // read 16 address bits +#define SAMSUNG_ID_OFFSET 17 // skip 16 + 1 sync bit +#define SAMSUNG_ID_LEN 4 // read 4 id bits +#define SAMSUNG_COMMAND_OFFSET 21 // skip 16 + 1 sync + 4 data bits +#define SAMSUNG_COMMAND_LEN 16 // read 16 command bits +#define SAMSUNG_COMPLETE_DATA_LEN 37 // complete length +#define SAMSUNG_STOP_BIT 1 // has stop bit +#define SAMSUNG_LSB 1 // LSB...MSB? +#define SAMSUNG_FLAGS 0 // flags + +#define SAMSUNG32_COMMAND_OFFSET 16 // skip 16 bits +#define SAMSUNG32_COMMAND_LEN 16 // read 16 command bits +#define SAMSUNG32_COMPLETE_DATA_LEN 32 // complete length +#define SAMSUNG32_FRAMES 1 // SAMSUNG32 sends each frame 1 times +#define SAMSUNG32_AUTO_REPETITION_PAUSE_TIME 47.0e-3 // repetition after 47 ms +#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 47ms + +#define MATSUSHITA_START_BIT_PULSE_TIME 3488.0e-6 // 3488 usec pulse +#define MATSUSHITA_START_BIT_PAUSE_TIME 3488.0e-6 // 3488 usec pause +#define MATSUSHITA_PULSE_TIME 872.0e-6 // 872 usec pulse +#define MATSUSHITA_1_PAUSE_TIME 2616.0e-6 // 2616 usec pause +#define MATSUSHITA_0_PAUSE_TIME 872.0e-6 // 872 usec pause +#define MATSUSHITA_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define MATSUSHITA_ADDRESS_OFFSET 12 // skip 12 bits +#define MATSUSHITA_ADDRESS_LEN 12 // read 12 address bits +#define MATSUSHITA_COMMAND_OFFSET 0 // skip 0 bits +#define MATSUSHITA_COMMAND_LEN 12 // read 12 bits (6 custom + 6 command) +#define MATSUSHITA_COMPLETE_DATA_LEN 24 // complete length +#define MATSUSHITA_STOP_BIT 1 // has stop bit +#define MATSUSHITA_LSB 1 // LSB...MSB? +#define MATSUSHITA_FLAGS 0 // flags + +#define KASEIKYO_START_BIT_PULSE_TIME 3380.0e-6 // 3380 usec pulse +#define KASEIKYO_START_BIT_PAUSE_TIME 1690.0e-6 // 1690 usec pause +#define KASEIKYO_PULSE_TIME 423.0e-6 // 525 usec pulse +#define KASEIKYO_1_PAUSE_TIME 1269.0e-6 // 525 usec pause +#define KASEIKYO_0_PAUSE_TIME 423.0e-6 // 1690 usec pause +#define KASEIKYO_AUTO_REPETITION_PAUSE_TIME 74.0e-3 // repetition after 74 ms +#define KASEIKYO_FRAME_REPEAT_PAUSE_TIME 74.0e-3 // frame repeat after 74 ms +#define KASEIKYO_ADDRESS_OFFSET 0 // skip 0 bits +#define KASEIKYO_ADDRESS_LEN 16 // read 16 address bits +#define KASEIKYO_COMMAND_OFFSET 28 // skip 28 bits (16 manufacturer & 4 parity & 8 genre) +#define KASEIKYO_COMMAND_LEN 12 // read 12 command bits (10 real command & 2 id) +#define KASEIKYO_COMPLETE_DATA_LEN 48 // complete length +#define KASEIKYO_STOP_BIT 1 // has stop bit +#define KASEIKYO_LSB 1 // LSB...MSB? +#define KASEIKYO_FRAMES 2 // KASEIKYO sends 1st frame 2 times +#define KASEIKYO_FLAGS 0 // flags + +#define RECS80_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80_START_BIT_PAUSE_TIME 7432.0e-6 // 7432 usec pause +#define RECS80_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause +#define RECS80_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause +#define RECS80_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define RECS80_ADDRESS_OFFSET 1 // skip 1 bit (toggle bit) +#define RECS80_ADDRESS_LEN 3 // read 3 address bits +#define RECS80_COMMAND_OFFSET 4 // skip 4 bits (1 toggle + 3 address) +#define RECS80_COMMAND_LEN 6 // read 6 command bits +#define RECS80_COMPLETE_DATA_LEN 10 // complete length +#define RECS80_STOP_BIT 1 // has stop bit +#define RECS80_LSB 0 // MSB...LSB +#define RECS80_FLAGS 0 // flags + +#define RC5_BIT_TIME 889.0e-6 // 889 usec pulse/pause +#define RC5_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms + +#define RC5_ADDRESS_OFFSET 1 // skip 1 bit (2nd start) +#define RC5_ADDRESS_LEN 6 // read 1 toggle bit (for key repetition detection) + 5 address bits +#define RC5_COMMAND_OFFSET 7 // skip 5 bits (2nd start + 1 toggle + 5 address) +#define RC5_COMMAND_LEN 6 // read 6 command bits +#define RC5_COMPLETE_DATA_LEN 13 // complete length +#define RC5_STOP_BIT 0 // has no stop bit +#define RC5_LSB 0 // MSB...LSB +#define RC5_FLAGS IRMP_PARAM_FLAG_IS_MANCHESTER // flags + +#define DENON_PULSE_TIME 310.0e-6 // 310 usec pulse in practice, 275 in theory +#define DENON_1_PAUSE_TIME 1780.0e-6 // 1780 usec pause in practice, 1900 in theory +#define DENON_0_PAUSE_TIME 745.0e-6 // 745 usec pause in practice, 775 in theory +#define DENON_FRAMES 2 // DENON sends each frame 2 times +#define DENON_AUTO_REPETITION_PAUSE_TIME 65.0e-3 // inverted repetition after 65ms +#define DENON_FRAME_REPEAT_PAUSE_TIME 65.0e-3 // frame repeat after 65ms +#define DENON_ADDRESS_OFFSET 0 // skip 0 bits +#define DENON_ADDRESS_LEN 5 // read 5 address bits +#define DENON_COMMAND_OFFSET 5 // skip 5 +#define DENON_COMMAND_LEN 10 // read 10 command bits +#define DENON_COMPLETE_DATA_LEN 15 // complete length +#define DENON_STOP_BIT 1 // has stop bit +#define DENON_LSB 0 // MSB...LSB +#define DENON_FLAGS 0 // flags + +#define RC6_START_BIT_PULSE_TIME 2666.0e-6 // 2.666 msec pulse +#define RC6_START_BIT_PAUSE_TIME 889.0e-6 // 889 usec pause +#define RC6_TOGGLE_BIT_TIME 889.0e-6 // 889 msec pulse/pause +#define RC6_BIT_TIME 444.0e-6 // 889 usec pulse/pause +#define RC6_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define RC6_ADDRESS_OFFSET 5 // skip "1" + 3 mode bits + 1 toggle bit +#define RC6_ADDRESS_LEN 8 // read 8 address bits +#define RC6_COMMAND_OFFSET 13 // skip 12 bits ("1" + 3 mode + 1 toggle + 8 address) +#define RC6_COMMAND_LEN 8 // read 8 command bits +#define RC6_COMPLETE_DATA_LEN_SHORT 21 // complete length +#define RC6_COMPLETE_DATA_LEN_LONG 36 // complete length +#define RC6_STOP_BIT 0 // has no stop bit +#define RC6_LSB 0 // MSB...LSB +#define RC6_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags + +#define RECS80EXT_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80EXT_START_BIT_PAUSE_TIME 3637.0e-6 // 3637 usec pause +#define RECS80EXT_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80EXT_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause +#define RECS80EXT_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause +#define RECS80EXT_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define RECS80EXT_ADDRESS_OFFSET 2 // skip 2 bits (2nd start + 1 toggle) +#define RECS80EXT_ADDRESS_LEN 4 // read 4 address bits +#define RECS80EXT_COMMAND_OFFSET 6 // skip 6 bits (2nd start + 1 toggle + 4 address) +#define RECS80EXT_COMMAND_LEN 6 // read 6 command bits +#define RECS80EXT_COMPLETE_DATA_LEN 12 // complete length +#define RECS80EXT_STOP_BIT 1 // has stop bit +#define RECS80EXT_LSB 0 // MSB...LSB +#define RECS80EXT_FLAGS 0 // flags + +#define NUBERT_START_BIT_PULSE_TIME 1340.0e-6 // 1340 usec pulse +#define NUBERT_START_BIT_PAUSE_TIME 340.0e-6 // 340 usec pause +#define NUBERT_1_PULSE_TIME 1340.0e-6 // 1340 usec pulse +#define NUBERT_1_PAUSE_TIME 340.0e-6 // 340 usec pause +#define NUBERT_0_PULSE_TIME 500.0e-6 // 500 usec pulse +#define NUBERT_0_PAUSE_TIME 1300.0e-6 // 1300 usec pause +#define NUBERT_FRAMES 2 // Nubert sends 2 frames +#define NUBERT_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms +#define NUBERT_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 45ms +#define NUBERT_ADDRESS_OFFSET 0 // skip 0 bits +#define NUBERT_ADDRESS_LEN 0 // read 0 address bits +#define NUBERT_COMMAND_OFFSET 0 // skip 0 bits +#define NUBERT_COMMAND_LEN 10 // read 10 bits +#define NUBERT_COMPLETE_DATA_LEN 10 // complete length +#define NUBERT_STOP_BIT 1 // has stop bit +#define NUBERT_LSB 0 // MSB? +#define NUBERT_FLAGS 0 // flags + +#define BANG_OLUFSEN_START_BIT1_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT1_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_START_BIT2_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT2_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_START_BIT3_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT3_PAUSE_TIME 15625.0e-6 // 15625 usec pause +#define BANG_OLUFSEN_START_BIT4_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT4_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_1_PAUSE_TIME 9375.0e-6 // 9375 usec pause +#define BANG_OLUFSEN_0_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_R_PAUSE_TIME 6250.0e-6 // 6250 usec pause (repeat last bit) +#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME 12500.0e-6 // 12500 usec pause (trailer bit) +#define BANG_OLUFSEN_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define BANG_OLUFSEN_ADDRESS_OFFSET 0 // no address bits +#define BANG_OLUFSEN_ADDRESS_LEN 0 // no address bits +#define BANG_OLUFSEN_COMMAND_OFFSET 3 // skip startbits 2, 3, 4 +#define BANG_OLUFSEN_COMMAND_LEN 16 // read 16 command bits +#define BANG_OLUFSEN_COMPLETE_DATA_LEN 20 // complete length: startbits 2, 3, 4 + 16 data bits + trailer bit +#define BANG_OLUFSEN_STOP_BIT 1 // has stop bit +#define BANG_OLUFSEN_LSB 0 // MSB...LSB +#define BANG_OLUFSEN_FLAGS 0 // flags + +#define GRUNDIG_NOKIA_IR60_BIT_TIME 528.0e-6 // 528 usec pulse/pause +#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME 2639.0e-6 // 2639 usec pause after pre bit +#define GRUNDIG_NOKIA_IR60_FRAME_REPEAT_PAUSE_TIME 117.76e-3 // info frame repeat after 117.76 ms +#define GRUNDIG_NOKIA_IR60_STOP_BIT 0 // has no stop bit +#define GRUNDIG_NOKIA_IR60_LSB 1 // MSB...LSB +#define GRUNDIG_NOKIA_IR60_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags + +#define GRUNDIG_FRAMES 2 // GRUNDIG sends each frame 1+1 times +#define GRUNDIG_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms +#define GRUNDIG_ADDRESS_OFFSET 0 // no address +#define GRUNDIG_ADDRESS_LEN 0 // no address +#define GRUNDIG_COMMAND_OFFSET 1 // skip 1 start bit +#define GRUNDIG_COMMAND_LEN 9 // read 9 command bits +#define GRUNDIG_COMPLETE_DATA_LEN 10 // complete length: 1 start bit + 9 data bits + +#define NOKIA_FRAMES 3 // NOKIA sends each frame 1 + 1 + 1 times +#define NOKIA_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms +#define NOKIA_ADDRESS_OFFSET 9 // skip 9 bits (1 start bit + 8 data bits) +#define NOKIA_ADDRESS_LEN 8 // 7 address bits +#define NOKIA_COMMAND_OFFSET 1 // skip 1 bit (1 start bit) +#define NOKIA_COMMAND_LEN 8 // read 8 command bits +#define NOKIA_COMPLETE_DATA_LEN 17 // complete length: 1 start bit + 8 address bits + 8 command bits + +#define IR60_TIMEOUT_TIME 5000.0e-6 // timeout grundig frame, switch to IR60 +#define IR60_ADDRESS_OFFSET 0 // skip 1 bits +#define IR60_ADDRESS_LEN 0 // read 0 address bits +#define IR60_COMMAND_OFFSET 0 // skip 1 bit (start bit after pre bit, always 1) +#define IR60_COMMAND_LEN 7 // read 6 command bits +#define IR60_COMPLETE_DATA_LEN 7 // complete length + +#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME 275.0e-6 // 275 usec pulse +#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME 550.0e-6 // 550 usec pause +#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME 275.0e-6 // 275 usec short pulse +#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2 550.0e-6 // 550 usec long pulse +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME 275.0e-6 // 275 usec short pause +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2 550.0e-6 // 550 usec long pause +#define SIEMENS_OR_RUWIDO_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define SIEMENS_OR_RUWIDO_STOP_BIT 0 // has no stop bit +#define SIEMENS_OR_RUWIDO_LSB 0 // MSB...LSB +#define SIEMENS_OR_RUWIDO_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags + +#define RUWIDO_ADDRESS_OFFSET 0 // skip 0 bits +#define RUWIDO_ADDRESS_LEN 9 // read 9 address bits +#define RUWIDO_COMMAND_OFFSET 9 // skip 9 bits +#define RUWIDO_COMMAND_LEN 8 // read 7 + 1 command bits, last bit is only check bit +#define RUWIDO_COMPLETE_DATA_LEN 17 // complete length + +#define SIEMENS_ADDRESS_OFFSET 0 // skip 0 bits +#define SIEMENS_ADDRESS_LEN 11 // read 11 bits +#define SIEMENS_COMMAND_OFFSET 11 // skip 11 bits +#define SIEMENS_COMMAND_LEN 11 // read 10 + 1 command bits, last bit is only check bit +#define SIEMENS_COMPLETE_DATA_LEN 22 // complete length + +#define FDC_START_BIT_PULSE_TIME 2085.0e-6 // 2085 usec pulse +#define FDC_START_BIT_PAUSE_TIME 966.0e-6 // 966 usec pause +#define FDC_PULSE_TIME 300.0e-6 // 300 usec pulse +#define FDC_1_PAUSE_TIME 715.0e-6 // 715 usec pause +#define FDC_0_PAUSE_TIME 220.0e-6 // 220 usec pause +#define FDC_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms +#define FDC_ADDRESS_OFFSET 0 // skip 0 bits +#define FDC_ADDRESS_LEN 14 // read 14 address bits, but use only 6, shift 8 into command +#define FDC_COMMAND_OFFSET 20 // skip 20 bits +#define FDC_COMMAND_LEN 12 // read 12 bits +#define FDC_COMPLETE_DATA_LEN 40 // complete length +#define FDC_STOP_BIT 1 // has stop bit +#define FDC_LSB 1 // LSB...MSB +#define FDC_FLAGS 0 // flags + +#define RCCAR_START_BIT_PULSE_TIME 2000.0e-6 // 2000 usec pulse +#define RCCAR_START_BIT_PAUSE_TIME 2000.0e-6 // 2000 usec pause +#define RCCAR_PULSE_TIME 600.0e-6 // 360 usec pulse +#define RCCAR_1_PAUSE_TIME 450.0e-6 // 650 usec pause +#define RCCAR_0_PAUSE_TIME 900.0e-6 // 180 usec pause +#define RCCAR_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms +#define RCCAR_ADDRESS_OFFSET 0 // skip 0 bits +#define RCCAR_ADDRESS_LEN 0 // read 0 address bits +#define RCCAR_COMMAND_OFFSET 0 // skip 0 bits +#define RCCAR_COMMAND_LEN 13 // read 13 bits +#define RCCAR_COMPLETE_DATA_LEN 13 // complete length +#define RCCAR_STOP_BIT 1 // has stop bit +#define RCCAR_LSB 1 // LSB...MSB +#define RCCAR_FLAGS 0 // flags + +#define JVC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse +#define JVC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define JVC_PULSE_TIME 560.0e-6 // 560 usec pulse +#define JVC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause +#define JVC_0_PAUSE_TIME 560.0e-6 // 560 usec pause +#define JVC_FRAME_REPEAT_PAUSE_TIME 22.0e-3 // frame repeat after 22ms +#define JVC_ADDRESS_OFFSET 0 // skip 0 bits +#define JVC_ADDRESS_LEN 4 // read 4 address bits +#define JVC_COMMAND_OFFSET 4 // skip 4 bits +#define JVC_COMMAND_LEN 12 // read 12 bits +#define JVC_COMPLETE_DATA_LEN 16 // complete length +#define JVC_STOP_BIT 1 // has stop bit +#define JVC_LSB 1 // LSB...MSB +#define JVC_FLAGS 0 // flags + +#define NIKON_START_BIT_PULSE_TIME 2200.0e-6 // 2200 usec pulse +#define NIKON_START_BIT_PAUSE_TIME 27100.0e-6 // 27100 usec pause +#define NIKON_PULSE_TIME 500.0e-6 // 500 usec pulse +#define NIKON_1_PAUSE_TIME 3500.0e-6 // 3500 usec pause +#define NIKON_0_PAUSE_TIME 1500.0e-6 // 1500 usec pause +#define NIKON_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms +#define NIKON_ADDRESS_OFFSET 0 // skip 0 bits +#define NIKON_ADDRESS_LEN 0 // read 0 address bits +#define NIKON_COMMAND_OFFSET 0 // skip 0 bits +#define NIKON_COMMAND_LEN 2 // read 2 bits +#define NIKON_COMPLETE_DATA_LEN 2 // complete length +#define NIKON_STOP_BIT 1 // has stop bit +#define NIKON_LSB 0 // LSB...MSB +#define NIKON_FLAGS 0 // flags + +#define KATHREIN_START_BIT_PULSE_TIME 210.0e-6 // 1340 usec pulse +#define KATHREIN_START_BIT_PAUSE_TIME 6218.0e-6 // 340 usec pause +#define KATHREIN_1_PULSE_TIME 210.0e-6 // 1340 usec pulse +#define KATHREIN_1_PAUSE_TIME 3000.0e-6 // 340 usec pause +#define KATHREIN_0_PULSE_TIME 210.0e-6 // 500 usec pulse +#define KATHREIN_0_PAUSE_TIME 1400.0e-6 // 1300 usec pause +#define KATHREIN_SYNC_BIT_PAUSE_LEN_TIME 4600.0e-6 // 4600 usec sync (on 6th and/or 8th bit) +#define KATHREIN_FRAMES 1 // Kathrein sends 1 frame +#define KATHREIN_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms +#define KATHREIN_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms +#define KATHREIN_ADDRESS_OFFSET 1 // skip 1 bits +#define KATHREIN_ADDRESS_LEN 4 // read 4 address bits +#define KATHREIN_COMMAND_OFFSET 5 // skip 5 bits +#define KATHREIN_COMMAND_LEN 7 // read 7 bits +#define KATHREIN_COMPLETE_DATA_LEN 13 // complete length +#define KATHREIN_STOP_BIT 1 // has stop bit +#define KATHREIN_LSB 0 // MSB +#define KATHREIN_FLAGS 0 // flags + +#define NETBOX_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse +#define NETBOX_START_BIT_PAUSE_TIME 800.0e-6 // 800 usec pause +#define NETBOX_PULSE_TIME 800.0e-6 // 800 usec pulse +#define NETBOX_PAUSE_TIME 800.0e-6 // 800 usec pause +#define NETBOX_FRAMES 1 // Netbox sends 1 frame +#define NETBOX_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms +#define NETBOX_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms +#define NETBOX_ADDRESS_OFFSET 0 // skip 0 bits +#define NETBOX_ADDRESS_LEN 3 // read 3 address bits +#define NETBOX_COMMAND_OFFSET 3 // skip 3 bits +#define NETBOX_COMMAND_LEN 13 // read 13 bits +#define NETBOX_COMPLETE_DATA_LEN 16 // complete length +#define NETBOX_STOP_BIT 0 // has no stop bit +#define NETBOX_LSB 1 // LSB +#define NETBOX_FLAGS IRMP_PARAM_FLAG_IS_SERIAL // flags + +#define LEGO_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz) +#define LEGO_START_BIT_PAUSE_TIME 1026.0e-6 // 1026 usec pause (39 x 1/38kHz) +#define LEGO_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz) +#define LEGO_1_PAUSE_TIME 553.0e-6 // 553 usec pause (21 x 1/38kHz) +#define LEGO_0_PAUSE_TIME 263.0e-6 // 263 usec pause (10 x 1/38kHz) +#define LEGO_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms +#define LEGO_ADDRESS_OFFSET 0 // skip 0 bits +#define LEGO_ADDRESS_LEN 0 // read 0 address bits +#define LEGO_COMMAND_OFFSET 0 // skip 0 bits +#define LEGO_COMMAND_LEN 16 // read 16 bits (12 command + 4 CRC) +#define LEGO_COMPLETE_DATA_LEN 16 // complete length +#define LEGO_STOP_BIT 1 // has stop bit +#define LEGO_LSB 0 // MSB...LSB +#define LEGO_FLAGS 0 // flags + +#define THOMSON_PULSE_TIME 550.0e-6 // 550 usec pulse +#define THOMSON_1_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define THOMSON_0_PAUSE_TIME 2000.0e-6 // 2000 usec pause +#define THOMSON_FRAMES 1 // THOMSON sends 1 frame +#define THOMSON_AUTO_REPETITION_PAUSE_TIME 65.0e-3 // repetition after 65ms +#define THOMSON_FRAME_REPEAT_PAUSE_TIME 65.0e-3 // frame repeat after 65ms +#define THOMSON_ADDRESS_OFFSET 0 // skip 0 bits +#define THOMSON_ADDRESS_LEN 4 // read 4 address bits +#define THOMSON_COMMAND_OFFSET 5 // skip 4 address bits + 1 toggle bit +#define THOMSON_COMMAND_LEN 7 // read 7 command bits +#define THOMSON_COMPLETE_DATA_LEN 12 // complete length +#define THOMSON_STOP_BIT 1 // has stop bit +#define THOMSON_LSB 0 // MSB...LSB +#define THOMSON_FLAGS 0 // flags + +#define GRUNDIG2_START_BIT_PULSE_TIME 550.0e-6 // 550 usec pulse +#define GRUNDIG2_START_BIT_PAUSE_TIME 2700.0e-6 // 2700 usec pause +#define GRUNDIG2_BIT_PULSE_TIME 550.0e-6 // 550 usec short pulse +#define GRUNDIG2_BIT_PAUSE_TIME 550.0e-6 // 550 usec short pause +#define GRUNDIG2_FRAME_REPEAT_PAUSE_TIME 100.0e-3 // frame repeat after 100ms +#define GRUNDIG2_STOP_BIT 0 // has no stop bit +#define GRUNDIG2_LSB 1 // MSB...LSB +#define GRUNDIG2_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags +#define GRUNDIG2_ADDRESS_OFFSET 0 // skip 0 bits +#define GRUNDIG2_ADDRESS_LEN 0 // read 0 bits +#define GRUNDIG2_COMMAND_OFFSET 0 // skip 0 bits +#define GRUNDIG2_COMMAND_LEN 7 // read 6 + 1 command bits, last bit is always 1 +#define GRUNDIG2_COMPLETE_DATA_LEN 7 // complete length + +#define AUTO_FRAME_REPETITION_TIME 80.0e-3 // SIRCS/SAMSUNG32/NUBERT: automatic repetition after 25-50ms + // KASEIKYO: automatic repetition after 75ms + +#define TRUE 1 +#define FALSE 0 + +#define IRMP_FLAG_REPETITION 0x01 + +typedef struct +{ + uint8_t protocol; // protocol, i.e. NEC_PROTOCOL + uint16_t address; // address + uint16_t command; // command + uint8_t flags; // flags, e.g. repetition +} IRMP_DATA; + +extern void irmp_init (void); +extern uint8_t irmp_get_data (IRMP_DATA *); +extern uint8_t irmp_is_busy (void); +extern uint8_t irmp_ISR (uint8_t); + +#if IRMP_PROTOCOL_NAMES == 1 +extern char * irmp_protocol_names[IRMP_N_PROTOCOLS + 1]; +#endif + +#if IRMP_USE_CALLBACK == 1 +extern void irmp_set_callback_ptr (void (*cb)(uint8_t)); +#endif // IRSND_USE_CALLBACK == 1 + +#endif /* _WC_IRMP_H_ */ diff --git a/libarmbox/irmpconfig.h b/libarmbox/irmpconfig.h new file mode 100644 index 0000000..10b34b7 --- /dev/null +++ b/libarmbox/irmpconfig.h @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * irmpconfig.h + * + * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de + * + * $Id: irmpconfig.h,v 1.80 2012/02/21 08:41:46 fm Exp $ + * + * ATMEGA88 @ 8 MHz + * + * 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; either version 2 of the License, or + * (at your option) any later version. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#ifndef _IRMPCONFIG_H_ +#define _IRMPCONFIG_H_ + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Change F_INTERRUPTS if you change the number of interrupts per second, + * Normally, F_INTERRUPTS should be in the range from 10000 to 15000, typical is 15000 + * A value above 15000 costs additional program space, absolute maximum value is 20000. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef F_INTERRUPTS +#define F_INTERRUPTS 15000 // interrupts per second, min: 10000, max: 20000, typ: 15000 +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Change settings from 1 to 0 if you want to disable one or more decoders. + * This saves program space. + * + * 1 enable decoder + * 0 disable decoder + * + * The standard decoders are enabled per default. + * Less common protocols are disabled here, you need to enable them manually. + * + * If you want to use FDC or RCCAR simultaneous with RC5 protocol, additional program space is required. + * If you don't need RC5 when using FDC/RCCAR, you should disable RC5. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +// typical protocols, disable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_SIRCS_PROTOCOL 1 // Sony SIRCS >= 10000 ~150 bytes +#define IRMP_SUPPORT_NEC_PROTOCOL 1 // NEC + APPLE >= 10000 ~300 bytes +#define IRMP_SUPPORT_SAMSUNG_PROTOCOL 1 // Samsung + Samsung32 >= 10000 ~300 bytes +#define IRMP_SUPPORT_MATSUSHITA_PROTOCOL 1 // Matsushita >= 10000 ~50 bytes +#define IRMP_SUPPORT_KASEIKYO_PROTOCOL 1 // Kaseikyo >= 10000 ~250 bytes +#define IRMP_SUPPORT_DENON_PROTOCOL 1 // DENON, Sharp >= 10000 ~250 bytes + +// more protocols, enable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_RC5_PROTOCOL 0 // RC5 >= 10000 ~250 bytes +#define IRMP_SUPPORT_RC6_PROTOCOL 0 // RC6 & RC6A >= 10000 ~250 bytes +#define IRMP_SUPPORT_JVC_PROTOCOL 0 // JVC >= 10000 ~150 bytes +#define IRMP_SUPPORT_NEC16_PROTOCOL 0 // NEC16 >= 10000 ~100 bytes +#define IRMP_SUPPORT_NEC42_PROTOCOL 0 // NEC42 >= 10000 ~300 bytes +#define IRMP_SUPPORT_IR60_PROTOCOL 0 // IR60 (SAB2008) >= 10000 ~300 bytes +#define IRMP_SUPPORT_GRUNDIG_PROTOCOL 0 // Grundig >= 10000 ~300 bytes +#define IRMP_SUPPORT_SIEMENS_PROTOCOL 0 // Siemens Gigaset >= 15000 ~550 bytes +#define IRMP_SUPPORT_NOKIA_PROTOCOL 0 // Nokia >= 10000 ~300 bytes + +// exotic protocols, enable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_GRUNDIG2_PROTOCOL 0 // Grundig TP400 >= 10000 ~300 bytes +#define IRMP_SUPPORT_KATHREIN_PROTOCOL 0 // Kathrein >= 10000 ~200 bytes +#define IRMP_SUPPORT_NUBERT_PROTOCOL 0 // NUBERT >= 10000 ~50 bytes +#define IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL 0 // Bang & Olufsen >= 10000 ~200 bytes +#define IRMP_SUPPORT_RECS80_PROTOCOL 0 // RECS80 (SAA3004) >= 15000 ~50 bytes +#define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0 // RECS80EXT (SAA3008) >= 15000 ~50 bytes +#define IRMP_SUPPORT_THOMSON_PROTOCOL 0 // Thomson >= 10000 ~250 bytes +#define IRMP_SUPPORT_NIKON_PROTOCOL 0 // NIKON camera >= 10000 ~250 bytes +#define IRMP_SUPPORT_NETBOX_PROTOCOL 0 // Netbox keyboard >= 10000 ~400 bytes (PROTOTYPE!) +#define IRMP_SUPPORT_FDC_PROTOCOL 0 // FDC3402 keyboard >= 10000 (better 15000) ~150 bytes (~400 in combination with RC5) +#define IRMP_SUPPORT_RCCAR_PROTOCOL 0 // RC Car >= 10000 (better 15000) ~150 bytes (~500 in combination with RC5) +#define IRMP_SUPPORT_RUWIDO_PROTOCOL 0 // RUWIDO, T-Home >= 15000 ~550 bytes +#define IRMP_SUPPORT_LEGO_PROTOCOL 0 // LEGO Power RC >= 20000 ~150 bytes + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Change hardware pin here: + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if defined (PIC_C18) // Microchip C18 Compiler +#include // main PIC18 h file +#define IRMP_PIN PORTBbits.RB4 // use RB4 as IR input on PIC +#define input(x) (x) + +#elif defined (PIC_CCS_COMPILER) // PIC CCS Compiler: +#define IRMP_PIN PIN_B4 // use PB4 as IR input on PIC + +#else // AVR: + +#ifndef ARDUINO +#define IRMP_PORT PORTB +#define IRMP_DDR DDRB +#define IRMP_PIN PINB +#define IRMP_BIT 6 // use PB6 as IR input on AVR +#else // ARDUINO +#define IRMP_PIN PIND // use digital pin 2 as IR input +#define IRMP_BIT 2 // on arduino +#endif // ARDUINO + +#define input(x) ((x) & (1 << IRMP_BIT)) +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Set IRMP_LOGGING to 1 if want to log data to UART with 9600Bd + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef IRMP_LOGGING +#define IRMP_LOGGING 0 // 1: log IR signal (scan), 0: do not (default) +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Use external logging routines + * If you enable external logging, you have also to enable IRMP_LOGGING above + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef IRMP_EXT_LOGGING +#define IRMP_EXT_LOGGING 0 // 1:log, 0: do not log ; +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Set IRMP_PROTOCOL_NAMES to 1 if want to access protocol names (for logging etc), costs ~300 bytes RAM! + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#define IRMP_PROTOCOL_NAMES 0 // 1: access protocol names, 0: do not (default), + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Use Callbacks to indicate input signal + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#define IRMP_USE_CALLBACK 0 // flag: 0 = don't use callbacks, 1 = use callbacks, default is 0 + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * DO NOT CHANGE THE FOLLOWING LINES ! + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, SIEMENS protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_SIEMENS_PROTOCOL +# define IRMP_SUPPORT_SIEMENS_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, RUWIDO protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_RUWIDO_PROTOCOL +# define IRMP_SUPPORT_RUWIDO_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, RECS80 protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_RECS80_PROTOCOL +# define IRMP_SUPPORT_RECS80_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, RECS80EXT protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_RECS80EXT_PROTOCOL +# define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 && F_INTERRUPTS < 20000 +# warning F_INTERRUPTS too low, LEGO protocol disabled (should be at least 20000) +# undef IRMP_SUPPORT_LEGO_PROTOCOL +# define IRMP_SUPPORT_LEGO_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0 +# warning JVC protocol needs also NEC protocol, NEC protocol enabled +# undef IRMP_SUPPORT_NEC_PROTOCOL +# define IRMP_SUPPORT_NEC_PROTOCOL 1 +#endif + +#if IRMP_SUPPORT_NEC16_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0 +# warning NEC16 protocol needs also NEC protocol, NEC protocol enabled +# undef IRMP_SUPPORT_NEC_PROTOCOL +# define IRMP_SUPPORT_NEC_PROTOCOL 1 +#endif + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0 +# warning NEC42 protocol needs also NEC protocol, NEC protocol enabled +# undef IRMP_SUPPORT_NEC_PROTOCOL +# define IRMP_SUPPORT_NEC_PROTOCOL 1 +#endif + +#if F_INTERRUPTS > 20000 +#error F_INTERRUPTS too high (should be not greater than 20000) +#endif + +#endif /* _WC_IRMPCONFIG_H_ */ diff --git a/libarmbox/lirmp_input.cpp b/libarmbox/lirmp_input.cpp new file mode 100644 index 0000000..73dcc06 --- /dev/null +++ b/libarmbox/lirmp_input.cpp @@ -0,0 +1,445 @@ +/* + * Simulate a linux input device via uinput + * Get lirc remote events, decode with IRMP and inject them via uinput + * + * (C) 2012 Stefan Seyfried + * + * 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; 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, see . + */ + +/* the C++ compiler did not like this code, so let's put it into a + * separate file and compile with gcc insead of g++... + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "lirmp_input.h" +extern "C" { +#include "irmp.h" +} +static uint8_t IRMP_PIN; + +#include +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args) + +/* same defines as in neutrino's rcinput.h */ +#define KEY_TTTV KEY_FN_1 +#define KEY_TTZOOM KEY_FN_2 +#define KEY_REVEAL KEY_FN_D +/* only defined in newer kernels / headers... */ +#ifndef KEY_ZOOMIN +#define KEY_ZOOMIN KEY_FN_E +#endif +#ifndef KEY_ZOOMOUT +#define KEY_ZOOMOUT KEY_FN_F +#endif + +typedef struct { + uint16_t ir; /* IR command */ + int code; /* input key code */ +} key_map_t; + +static const key_map_t key_map[] = { + { 0x13, KEY_0 }, + { 0x1a, KEY_1 }, + { 0x1f, KEY_2 }, + { 0x58, KEY_3 }, + { 0x16, KEY_4 }, + { 0x1b, KEY_5 }, + { 0x54, KEY_6 }, + { 0x12, KEY_7 }, + { 0x17, KEY_8 }, + { 0x50, KEY_9 }, + { 0x5f, KEY_OK }, + { 0x59, KEY_TIME }, + { 0x43, KEY_FAVORITES }, + { 0x4f, KEY_SAT }, + { 0x0f, KEY_NEXT }, /* V.Format */ + { 0x1e, KEY_POWER }, + { 0x5a, KEY_MUTE }, + { 0x1c, KEY_MENU }, + { 0x5d, KEY_EPG }, + { 0x07, KEY_INFO }, + { 0x60, KEY_EXIT }, + { 0x48, KEY_PAGEUP }, + { 0x44, KEY_PAGEDOWN }, + { 0x02, KEY_LEFT }, + { 0x40, KEY_RIGHT }, + { 0x03, KEY_UP }, + { 0x5e, KEY_DOWN }, + { 0x0a, KEY_VOLUMEUP }, + { 0x06, KEY_VOLUMEDOWN }, + { 0x49, KEY_RED }, + { 0x4e, KEY_GREEN }, + { 0x11, KEY_YELLOW }, + { 0x4a, KEY_BLUE }, + { 0x4c, KEY_TV }, /* TV/Radio */ + { 0x5c, KEY_VIDEO }, /* FIND */ + { 0x19, KEY_AUDIO }, /* FOLDER */ +/* KEY_AUX, + KEY_TEXT, + KEY_TTTV, + KEY_TTZOOM, + KEY_REVEAL, +*/ + { 0x01, KEY_REWIND }, + { 0x53, KEY_FORWARD }, + { 0x22, KEY_STOP }, + { 0x4d, KEY_PAUSE }, + { 0x15, KEY_PLAY }, + { 0x20, KEY_PREVIOUS }, + { 0x23, KEY_NEXT }, +// KEY_EJECTCD, + { 0x10, KEY_RECORD } +}; + +static const int key_list[] = { + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_OK, + KEY_TIME, + KEY_FAVORITES, + KEY_SAT, + KEY_ZOOMOUT, + KEY_ZOOMIN, + KEY_NEXT, + KEY_POWER, + KEY_MUTE, + KEY_MENU, + KEY_EPG, + KEY_INFO, + KEY_EXIT, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_RED, + KEY_GREEN, + KEY_YELLOW, + KEY_BLUE, + KEY_TV, + KEY_VIDEO, + KEY_AUDIO, +// KEY_AUX, +// KEY_TEXT, +// KEY_TTTV, +// KEY_TTZOOM, +// KEY_REVEAL, + KEY_REWIND, + KEY_STOP, + KEY_PAUSE, + KEY_PLAY, + KEY_FORWARD, + KEY_PREVIOUS, + KEY_NEXT, +// KEY_EJECTCD, + KEY_RECORD, + -1 +}; + +static pthread_t thread; +static int thread_running; + +static void *input_thread(void *) +{ + int uinput; + struct input_event u; + struct uinput_user_dev ud; + FILE *f; + int lircfd; + int pulse; + int i = 0; + int last_pulse = 1; + int last_code = -1; + uint32_t lircdata; /* lirc_t to be correct... */ + unsigned int count = 0; /* how many timeouts? */ + unsigned int nodec = 0; /* how many timeouts since last decoded? */ + int aotom_fd = -1; + IRMP_DATA d; + + lt_info("LIRC/IRMP input converter thread starting...\n"); + + /* modprobe does not complain if the module is already loaded... */ + system("/sbin/modprobe uinput"); + do { + usleep(100000); /* mdev needs some time to create the device? */ + uinput = open("/dev/uinput", O_WRONLY|O_NDELAY); + } while (uinput < 0 && ++count < 100); + + if (uinput < 0) + { + lt_info("LIRC/IRMP input thread: unable to open /dev/uinput (%m)\n"); + thread_running = 2; + return NULL; + } + + fcntl(uinput, F_SETFD, FD_CLOEXEC); + ioctl(uinput, UI_SET_EVBIT, EV_KEY); + /* do not use kernel repeat EV_REP since neutrino will be confused by the + * generated SYN_REPORT events... + ioctl(uinput, UI_SET_EVBIT, EV_REP); + */ + /* register keys */ + for (i = 0; key_list[i] != -1; i++) + ioctl(uinput, UI_SET_KEYBIT, key_list[i]); + + /* configure the device */ + memset(&ud, 0, sizeof(ud)); + strncpy(ud.name, "Neutrino LIRC/IRMP to Input Device converter", UINPUT_MAX_NAME_SIZE); + ud.id.version = 0x42; + ud.id.vendor = 0x1234; + ud.id.product = 0x5678; + ud.id.bustype = BUS_I2C; /* ?? */ + write(uinput, &ud, sizeof(ud)); + + if (ioctl(uinput, UI_DEV_CREATE)) + { + lt_info("LIRC/IRMP input thread UI_DEV_CREATE: %m\n"); + close(uinput); + return NULL; + } + + /* this is ugly: parse the new input device from /proc/...devices + * and symlink it to /dev/input/nevis_ir... */ +#define DEVLINE "I: Bus=0018 Vendor=1234 Product=5678 Version=0042" + f = fopen("/proc/bus/input/devices", "r"); + if (f) + { + int found = 0; + int evdev = -1; + size_t n = 0; + char *line = NULL; + char *p; + char newdev[20]; + while (getline(&line, &n, f) != -1) + { + switch(line[0]) + { + case 'I': + if (strncmp(line, DEVLINE, strlen(DEVLINE)) == 0) + found = 1; + break; + case 'H': + if (! found) + break; + p = strstr(line, " event"); + if (! p) + { + evdev = -1; + break; + } + evdev = atoi(p + 6); + sprintf(newdev, "event%d", evdev); + lt_info("LIRC/IRMP input thread: symlink /dev/input/nevis_ir to %s\n", newdev); + unlink("/dev/input/nevis_ir"); + symlink(newdev, "/dev/input/nevis_ir"); + break; + default: + break; + } + if (evdev != -1) + break; + } + fclose(f); + free(line); + } + + u.type = EV_KEY; + u.value = 0; /* initialize: first event wil be a key press */ + + lircfd = open("/dev/lirc", O_RDONLY); + if (lircfd < 0) + { + lt_info("%s: open /dev/lirc: %m\n", __func__); + goto out; + } + IRMP_PIN = 0xFF; + +/* 50 ms. This should be longer than the longest light pulse */ +#define POLL_MS (100 * 1000) +#define LIRC_PULSE 0x01000000 +#define LIRC_PULSE_MASK 0x00FFFFFF + lt_info("LIRC/IRMP input converter going into main loop...\n"); + + aotom_fd = open("/dev/vfd", O_RDONLY); + + /* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */ + thread_running = 1; + while (thread_running) + { + fd_set fds; + struct timeval tv; + int ret; + + FD_ZERO(&fds); + FD_SET(lircfd, &fds); + tv.tv_sec = 0; + tv.tv_usec = POLL_MS; + /* any singal can interrupt select. we rely on the linux-only feature + * that the timeout is automatcally recalculated in this case! */ + do { + ret = select(lircfd + 1, &fds, NULL, NULL, &tv); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* errno != EINTR... */ + lt_info("%s: lirmp: lircfd select: %m\n", __func__); + break; + } + + if (ret == 0) + { + count++; + nodec++; + lircdata = POLL_MS; /* timeout */ + pulse = !last_pulse; /* lirc sends data on signal change */ + if (last_code != -1 && nodec > 1) + { + // fprintf(stderr, "timeout!\n"); + u.code = last_code; + u.value = 0; /* release */ + write(uinput, &u, sizeof(u)); + last_code = -1; + } + } + else + { + if (read(lircfd, &lircdata, sizeof(lircdata)) != sizeof(lircdata)) + { + perror("read"); + break; + } + pulse = (lircdata & LIRC_PULSE); /* we got light... */ + last_pulse = pulse; + lircdata &= LIRC_PULSE_MASK; /* how long the pulse was in microseconds */ + } + + if (ret && count) + { + if (count * POLL_MS > lircdata) + lircdata = 0; + else + lircdata -= count * POLL_MS; + count = 0; + } + //printf("lircdata: ret:%d c:%d %d\n", ret, ch - '0', lircdata); + lircdata /= (1000000 / F_INTERRUPTS); + + if (pulse) + IRMP_PIN = 0x00; + else + IRMP_PIN = 0xff; + + do { + (void) irmp_ISR (IRMP_PIN); + if (irmp_get_data (&d)) + { + nodec = 0; + lt_debug("irmp_get_data proto: %2d addr: 0x%04x cmd: 0x%04x fl: %d\n", + d.protocol, d.address, d.command, d.flags); + + /* todo: do we need to complete the loop if we already + * detected the singal in this pulse? */ + if (d.protocol == IRMP_NEC_PROTOCOL && d.address == 0xba45) + { + for (i = 0; i < (int)(sizeof(key_map)/sizeof(key_map_t)); i++) + { + if (key_map[i].ir == d.command) + { + if (last_code != -1 && last_code != key_map[i].code) + { + u.code = last_code; + u.value = 0; + write(uinput, &u, sizeof(u)); + } + u.code = key_map[i].code; + u.value = (d.flags & 0x1) + 1; + //lt_debug("uinput write: value: %d code: %d\n", u.value, u.code); + last_code = u.code; + write(uinput, &u, sizeof(u)); + if (aotom_fd > -1) { + struct aotom_ioctl_data vfd_data; + vfd_data.u.led.led_nr = 1; + vfd_data.u.led.on = 10; + ioctl(aotom_fd, VFDSETLED, &vfd_data); + } + break; + } + } + } + } + } while (lircdata-- > 0); + } + /* clean up */ + close (lircfd); + + if (aotom_fd > -1) + close(aotom_fd); + + out: + ioctl(uinput, UI_DEV_DESTROY); + return NULL; +} + +void start_input_thread(void) +{ + if (pthread_create(&thread, 0, input_thread, NULL) != 0) + { + lt_info("%s: LIRC/IRMP input thread pthread_create: %m\n", __func__); + thread_running = 0; + return; + } + /* wait until the device is created before continuing */ + while (! thread_running) + usleep(1000); + if (thread_running == 2) /* failed... :-( */ + thread_running = 0; +} + +void stop_input_thread(void) +{ + if (! thread_running) + return; + thread_running = 0; + pthread_join(thread, NULL); +} diff --git a/libarmbox/lirmp_input.h b/libarmbox/lirmp_input.h new file mode 100644 index 0000000..c277dda --- /dev/null +++ b/libarmbox/lirmp_input.h @@ -0,0 +1,7 @@ +/* functions from lirmp_input.cpp */ + +#ifndef __LIRMP_INPUT_H_ +#define __LIRMP_INPUT_H_ +void start_input_thread(void); +void stop_input_thread(void); +#endif diff --git a/libarmbox/playback_gst.cpp b/libarmbox/playback_gst.cpp new file mode 100644 index 0000000..53d9a5c --- /dev/null +++ b/libarmbox/playback_gst.cpp @@ -0,0 +1,837 @@ +/* + * + * 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; 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include "dmx_lib.h" +#include "audio_lib.h" +#include "video_lib.h" + +#include "playback_gst.h" + +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(HAL_DEBUG_PLAYBACK, this, args) +#define lt_info(args...) _lt_info(HAL_DEBUG_PLAYBACK, this, args) +#define lt_debug_c(args...) _lt_debug(HAL_DEBUG_PLAYBACK, NULL, args) +#define lt_info_c(args...) _lt_info(HAL_DEBUG_PLAYBACK, NULL, args) + +static const char * FILENAME = "[playback.cpp]"; + +#include +#include + +typedef enum +{ + GST_PLAY_FLAG_VIDEO = 0x00000001, + GST_PLAY_FLAG_AUDIO = 0x00000002, + GST_PLAY_FLAG_TEXT = 0x00000004, + GST_PLAY_FLAG_VIS = 0x00000008, + GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, + GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, + GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, + GST_PLAY_FLAG_DOWNLOAD = 0x00000080, + GST_PLAY_FLAG_BUFFERING = 0x000000100 +} GstPlayFlags; + + +GstElement * m_gst_playbin = NULL; +GstElement * audioSink = NULL; +GstElement * videoSink = NULL; +gchar * uri = NULL; +GstTagList * m_stream_tags = 0; +static int end_eof = 0; + +gint match_sinktype(const GValue *velement, const gchar *type) +{ + GstElement *element = GST_ELEMENT_CAST(g_value_get_object(velement)); + return strcmp(g_type_name(G_OBJECT_TYPE(element)), type); +} + +GstBusSyncReply Gst_bus_call(GstBus * bus, GstMessage *msg, gpointer user_data) +{ + gchar * sourceName; + + // source + GstObject * source; + source = GST_MESSAGE_SRC(msg); + + if (!GST_IS_OBJECT(source)) + return GST_BUS_DROP; + + sourceName = gst_object_get_name(source); + + switch (GST_MESSAGE_TYPE(msg)) + { + case GST_MESSAGE_EOS: + { + g_message("End-of-stream"); + end_eof = 1; + break; + } + + case GST_MESSAGE_ERROR: + { + gchar * debug; + GError *err; + gst_message_parse_error(msg, &err, &debug); + g_free (debug); + lt_info_c( "%s:%s - GST_MESSAGE_ERROR: %s (%i) from %s\n", FILENAME, __FUNCTION__, err->message, err->code, sourceName ); + if ( err->domain == GST_STREAM_ERROR ) + { + if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) + { + if ( g_strrstr(sourceName, "videosink") ) + lt_info_c( "%s:%s - GST_MESSAGE_ERROR: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? + else if ( g_strrstr(sourceName, "audiosink") ) + lt_info_c( "%s:%s - GST_MESSAGE_ERROR: audioSink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? + } + } + g_error_free(err); + + end_eof = 1; // NOTE: just to exit + + break; + } + + case GST_MESSAGE_INFO: + { + gchar *debug; + GError *inf; + + gst_message_parse_info (msg, &inf, &debug); + g_free (debug); + if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE ) + { + if ( g_strrstr(sourceName, "videosink") ) + lt_info_c( "%s:%s - GST_MESSAGE_INFO: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? + } + g_error_free(inf); + break; + } + + case GST_MESSAGE_TAG: + { + GstTagList *tags, *result; + gst_message_parse_tag(msg, &tags); + + result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE); + if (result) + { + if (m_stream_tags) + gst_tag_list_free(m_stream_tags); + m_stream_tags = result; + } + + const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0); + if ( gv_image ) + { + GstBuffer *buf_image; + buf_image = gst_value_get_buffer (gv_image); + int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644); + if(fd >= 0) + { + GstMapInfo Info; + gst_buffer_map(buf_image, &Info,(GstMapFlags)( GST_MAP_READ)); + int ret = write(fd, Info.data, Info.size); + close(fd); + gst_buffer_unmap(buf_image, &Info); + lt_info_c( "%s:%s - GST_MESSAGE_INFO: cPlayback::state /tmp/.id3coverart %d bytes written\n", FILENAME, __FUNCTION__ , ret); + } + //FIXME: how shall playback handle this event??? + } + gst_tag_list_free(tags); + lt_debug_c( "%s:%s - GST_MESSAGE_INFO: update info tags\n", FILENAME, __FUNCTION__); //FIXME: how shall playback handle this event??? + break; + } + + case GST_MESSAGE_STATE_CHANGED: + { + if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) + break; + + GstState old_state, new_state; + gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); + + if(old_state == new_state) + break; + lt_info_c( "%s:%s - GST_MESSAGE_STATE_CHANGED: state transition %s -> %s\n", FILENAME, __FUNCTION__, gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); + + GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state); + + switch(transition) + { + case GST_STATE_CHANGE_NULL_TO_READY: + { + } break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + { + GstIterator *children; + if (audioSink) + { + gst_object_unref(GST_OBJECT(audioSink)); + audioSink = NULL; + } + + if (videoSink) + { + gst_object_unref(GST_OBJECT(videoSink)); + videoSink = NULL; + } + children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin)); + GValue r = G_VALUE_INIT; + gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBAudioSink"); + audioSink = GST_ELEMENT_CAST(g_value_dup_object (&r)); + g_value_unset (&r); + gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBVideoSink"); + videoSink = GST_ELEMENT_CAST(g_value_dup_object (&r)); + g_value_unset (&r); + gst_iterator_free(children); + + } break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + { + } break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + { + } break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + { + if (audioSink) + { + gst_object_unref(GST_OBJECT(audioSink)); + audioSink = NULL; + } + if (videoSink) + { + gst_object_unref(GST_OBJECT(videoSink)); + videoSink = NULL; + } + } break; + case GST_STATE_CHANGE_READY_TO_NULL: + { + } break; + } + break; + } + break; + default: + break; + } + + return GST_BUS_DROP; +} + + +cPlayback::cPlayback(int num) +{ + lt_info( "%s:%s\n", FILENAME, __FUNCTION__); + const gchar *nano_str; + guint major, minor, micro, nano; + + gst_init(NULL, NULL); + + gst_version (&major, &minor, µ, &nano); + + if (nano == 1) + nano_str = "(CVS)"; + else if (nano == 2) + nano_str = "(Prerelease)"; + else + nano_str = ""; + + lt_info( "%s:%s - This program is linked against GStreamer %d.%d.%d %s\n", + FILENAME, __FUNCTION__, + major, minor, micro, nano_str); + + mAudioStream = 0; + mSpeed = 0; + + playing = false; + playstate = STATE_STOP; +} + +cPlayback::~cPlayback() +{ + lt_info( "%s:%s\n", FILENAME, __FUNCTION__); + //FIXME: all deleting stuff is done in Close() +} + +//Used by Fileplay +bool cPlayback::Open(playmode_t PlayMode) +{ + lt_info("%s: PlayMode %d\n", __func__, PlayMode); + return true; +} + +// used by movieplay +void cPlayback::Close(void) +{ + lt_info( "%s:%s\n", FILENAME, __FUNCTION__); + + Stop(); + + // disconnect bus handler + if (m_gst_playbin) + { + // disconnect sync handler callback + GstBus * bus = gst_pipeline_get_bus(GST_PIPELINE (m_gst_playbin)); + gst_bus_set_sync_handler(bus, NULL, NULL, NULL); + gst_object_unref(bus); + lt_info( "%s:%s - GST bus handler closed\n", FILENAME, __FUNCTION__); + } + + if (m_stream_tags) + gst_tag_list_free(m_stream_tags); + + // close gst + if (m_gst_playbin) + { + if (audioSink) + { + gst_object_unref(GST_OBJECT(audioSink)); + audioSink = NULL; + + lt_info( "%s:%s - GST audio Sink closed\n", FILENAME, __FUNCTION__); + } + + if (videoSink) + { + gst_object_unref(GST_OBJECT(videoSink)); + videoSink = NULL; + + lt_info( "%s:%s - GST video Sink closed\n", FILENAME, __FUNCTION__); + } + + // unref m_gst_playbin + gst_object_unref (GST_OBJECT (m_gst_playbin)); + lt_info( "%s:%s - GST playbin closed\n", FILENAME, __FUNCTION__); + + m_gst_playbin = NULL; + } +} + +// start +bool cPlayback::Start(std::string filename, std::string headers) +{ + return Start((char*) filename.c_str(),0,0,0,0,0, headers); +} + +bool cPlayback::Start(char *filename, int /*vpid*/, int /*vtype*/, int /*apid*/, int /*ac3*/, int /*duration*/, std::string headers) +{ + lt_info( "%s:%s\n", FILENAME, __FUNCTION__); + + mAudioStream = 0; + + //create playback path + char file[400] = {""}; + bool isHTTP = false; + + if(!strncmp("http://", filename, 7)) + { + isHTTP = true; + } + else if(!strncmp("file://", filename, 7)) + { + isHTTP = false; + } + else if(!strncmp("upnp://", filename, 7)) + { + isHTTP = true; + } + else if(!strncmp("rtmp://", filename, 7)) + { + isHTTP = true; + } + else if(!strncmp("rtsp://", filename, 7)) + { + isHTTP = true; + } + else if(!strncmp("mms://", filename, 6)) + { + isHTTP = true; + } + else + strcat(file, "file://"); + + strcat(file, filename); + + if (isHTTP) + uri = g_uri_escape_string(filename, G_URI_RESERVED_CHARS_GENERIC_DELIMITERS, true); + else + uri = g_filename_to_uri(filename, NULL, NULL); + + lt_info("%s:%s - filename=%s\n", FILENAME, __FUNCTION__, filename); + + // create gst pipeline + m_gst_playbin = gst_element_factory_make ("playbin", "playbin"); + + if(m_gst_playbin) + { + lt_info("%s:%s - m_gst_playbin\n", FILENAME, __FUNCTION__); + + guint flags; + g_object_get(G_OBJECT (m_gst_playbin), "flags", &flags, NULL); + /* avoid video conversion, let the (hardware) sinks handle that */ + flags |= GST_PLAY_FLAG_NATIVE_VIDEO; + /* volume control is done by hardware */ + flags &= ~GST_PLAY_FLAG_SOFT_VOLUME; + + g_object_set(G_OBJECT (m_gst_playbin), "uri", uri, NULL); + g_object_set(G_OBJECT (m_gst_playbin), "flags", flags, NULL); + + //gstbus handler + GstBus * bus = gst_pipeline_get_bus( GST_PIPELINE(m_gst_playbin) ); + gst_bus_set_sync_handler(bus, Gst_bus_call, NULL, NULL); + gst_object_unref(bus); + + // state playing + gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING); + + playing = true; + playstate = STATE_PLAY; + } + else + { + lt_info("%s:%s - failed to create GStreamer pipeline!, sorry we can not play\n", FILENAME, __FUNCTION__); + playing = false; + + return false; + } + + g_free(uri); + + // set buffer size + /* increase the default 2 second / 2 MB buffer limitations to 5s / 5MB */ + int m_buffer_size = 5*1024*1024; + //g_object_set(G_OBJECT(m_gst_playbin), "buffer-duration", 5LL * GST_SECOND, NULL); + g_object_set(G_OBJECT(m_gst_playbin), "buffer-size", m_buffer_size, NULL); + + return true; +} + +bool cPlayback::Play(void) +{ + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + + if(playing == true) + return true; + + if(m_gst_playbin) + { + gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PLAYING); + + playing = true; + playstate = STATE_PLAY; + } + lt_info("%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + + return playing; +} + +bool cPlayback::Stop(void) +{ + if(playing == false) + return false; + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + + // stop + if(m_gst_playbin) + { + gst_element_set_state(m_gst_playbin, GST_STATE_NULL); + } + + playing = false; + + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + + playstate = STATE_STOP; + + return true; +} + +bool cPlayback::SetAPid(int pid , bool /*ac3*/) +{ + lt_info("%s: pid %i\n", __func__, pid); + + int current_audio; + + if(pid != mAudioStream) + { + g_object_set (G_OBJECT (m_gst_playbin), "current-audio", pid, NULL); + printf("%s: switched to audio stream %i\n", __FUNCTION__, pid); + mAudioStream = pid; + } + + return true; +} + +void cPlayback::trickSeek(int ratio) +{ + bool validposition = false; + gint64 pos = 0; + int position; + int duration; + + if( GetPosition(position, duration) ) + { + validposition = true; + pos = position; + } + + gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING); + + if (validposition) + { + if(ratio >= 0.0) + gst_element_seek(m_gst_playbin, ratio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1); + else + gst_element_seek(m_gst_playbin, ratio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos); + } +} + +bool cPlayback::SetSpeed(int speed) +{ + lt_info( "%s:%s speed %d\n", FILENAME, __FUNCTION__, speed); + + if(playing == false) + return false; + + if(m_gst_playbin) + { + // pause + if(speed == 0) + { + gst_element_set_state(m_gst_playbin, GST_STATE_PAUSED); + //trickSeek(0); + playstate = STATE_PAUSE; + } + // play/continue + else if(speed == 1) + { + trickSeek(1); + //gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING); + // + playstate = STATE_PLAY; + } + //ff + else if(speed > 1) + { + trickSeek(speed); + // + playstate = STATE_FF; + } + //rf + else if(speed < 0) + { + trickSeek(speed); + // + playstate = STATE_REW; + } + } + + mSpeed = speed; + + return true; +} + +bool cPlayback::SetSlow(int slow) +{ + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + + if(playing == false) + return false; + + if(m_gst_playbin) + { + trickSeek(0.5); + } + + playstate = STATE_SLOW; + + mSpeed = slow; + + return true; +} + +bool cPlayback::GetSpeed(int &speed) const +{ + speed = mSpeed; + + return true; +} + +// in milliseconds +bool cPlayback::GetPosition(int &position, int &duration) +{ + if(playing == false) + return false; + + //EOF + if(end_eof) + { + end_eof = 0; + return false; + } + + if(m_gst_playbin) + { + //position + GstFormat fmt = GST_FORMAT_TIME; //Returns time in nanosecs + + gint64 pts = 0; + unsigned long long int sec = 0; + + gst_element_query_position(m_gst_playbin, fmt, &pts); + position = pts / 1000000.0; + + // duration + GstFormat fmt_d = GST_FORMAT_TIME; //Returns time in nanosecs + double length = 0; + gint64 len; + + gst_element_query_duration(m_gst_playbin, fmt_d, &len); + length = len / 1000000.0; + if(length < 0) + length = 0; + + duration = (int)(length); + } + + return true; +} + +bool cPlayback::SetPosition(int position, bool absolute) +{ + lt_info("%s: pos %d abs %d playing %d\n", __func__, position, absolute, playing); + + if(playing == false) + return false; + + gint64 time_nanoseconds; + gint64 pos; + GstFormat fmt = GST_FORMAT_TIME; + + if(m_gst_playbin) + { + gst_element_query_position(m_gst_playbin, fmt, &pos); + time_nanoseconds = pos + (position * 1000000.0); + if(time_nanoseconds < 0) + time_nanoseconds = 0; + + gst_element_seek(m_gst_playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, time_nanoseconds, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + } + + return true; +} + +void cPlayback::FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string * language) +{ + lt_info( "%s:%s\n", FILENAME, __FUNCTION__); + + if(m_gst_playbin) + { + gint i, n_audio = 0; + //GstStructure * structure = NULL; + + // get audio + g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL); + printf("%s: %d audio\n", __FUNCTION__, n_audio); + + if(n_audio == 0) + return; + + for (i = 0; i < n_audio; i++) + { + // apids + apids[i]=i; + + GstPad * pad = 0; + g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad); + GstCaps * caps = gst_pad_get_current_caps(pad); + if (!caps) + continue; + + GstStructure * structure = gst_caps_get_structure(caps, 0); + //const gchar *g_type = gst_structure_get_name(structure); + + //if (!structure) + //return atUnknown; + //ac3flags[0] = 0; + + // ac3flags + if ( gst_structure_has_name (structure, "audio/mpeg")) + { + gint mpegversion, layer = -1; + + if (!gst_structure_get_int (structure, "mpegversion", &mpegversion)) + //return atUnknown; + ac3flags[i] = 0; + + switch (mpegversion) + { + case 1: + /* + { + gst_structure_get_int (structure, "layer", &layer); + if ( layer == 3 ) + return atMP3; + else + return atMPEG; + ac3flags[0] = 4; + break; + } + */ + ac3flags[i] = 4; + case 2: + //return atAAC; + ac3flags[i] = 5; + case 4: + //return atAAC; + ac3flags[i] = 5; + default: + //return atUnknown; + ac3flags[i] = 0; + } + } + else if ( gst_structure_has_name (structure, "audio/x-ac3") || gst_structure_has_name (structure, "audio/ac3") ) + //return atAC3; + ac3flags[i] = 1; + else if ( gst_structure_has_name (structure, "audio/x-dts") || gst_structure_has_name (structure, "audio/dts") ) + //return atDTS; + ac3flags[i] = 6; + else if ( gst_structure_has_name (structure, "audio/x-raw-int") ) + //return atPCM; + ac3flags[i] = 0; + + gst_caps_unref(caps); + } + + // numpids + *numpida=i; + } +} + +void cPlayback::getMeta() +{ + if(playing) + return; +} + +bool cPlayback::SyncAV(void) +{ + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + + if(playing == false ) + return false; + + return true; +} + +void cPlayback::RequestAbort() +{ +} + +void cPlayback::FindAllSubs(uint16_t *, unsigned short *, uint16_t *numpida, std::string *) +{ + printf("%s:%s\n", FILENAME, __func__); + *numpida = 0; +} + +void cPlayback::GetChapters(std::vector &positions, std::vector &titles) +{ + positions.clear(); + titles.clear(); +} + +bool cPlayback::SelectSubtitles(int pid) +{ + printf("%s:%s pid %i\n", FILENAME, __func__, pid); + return true; +} + +void cPlayback::GetMetadata(std::vector &keys, std::vector &values) +{ + keys.clear(); + values.clear(); +} + +void cPlayback::FindAllTeletextsubtitlePids(int *, unsigned int *numpids, std::string *, int *, int *) +{ + *numpids = 0; +} + +void cPlayback::FindAllSubtitlePids(int * /*pids*/, unsigned int *numpids, std::string * /*language*/) +{ + *numpids = 0; +} + +bool cPlayback::SetSubtitlePid(int /*pid*/) +{ + return true; +} + +void cPlayback::GetPts(uint64_t &/*pts*/) +{ +} + +bool cPlayback::SetTeletextPid(int /*pid*/) +{ + return true; +} + +uint64_t cPlayback::GetReadCount() +{ + return 0; +} + +int cPlayback::GetAPid(void) +{ + lt_info("%s\n", __func__); + return 0; +} + +int cPlayback::GetVPid(void) +{ + return 0; +} + +int cPlayback::GetSubtitlePid(void) +{ + return 0; +} + +AVFormatContext *cPlayback::GetAVFormatContext() +{ + return NULL; +} + +void cPlayback::ReleaseAVFormatContext() +{ +} diff --git a/libarmbox/playback_gst.h b/libarmbox/playback_gst.h new file mode 100644 index 0000000..25331bd --- /dev/null +++ b/libarmbox/playback_gst.h @@ -0,0 +1,97 @@ +/* + * + * 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; 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __PLAYBACK_CS_H +#define __PLAYBACK_CS_H + +#include +#include +#include + +#include + + +typedef enum { + STATE_STOP, + STATE_PLAY, + STATE_PAUSE, + STATE_FF, + STATE_REW, + STATE_SLOW +} playstate_t; + +typedef enum { + PLAYMODE_TS = 0, + PLAYMODE_FILE, +} playmode_t; + +struct AVFormatContext; + +class cPlayback +{ + private: + bool playing; + + int mSpeed; + int mAudioStream; + + public: + playstate_t playstate; + + cPlayback(int); + bool Open(playmode_t PlayMode); + void Close(void); + bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration, std::string headers = ""); + bool Start(std::string filename, std::string headers = ""); + bool Play(void); + bool SyncAV(void); + + bool Stop(void); + bool SetAPid(int pid, bool ac3); + bool SetSubtitlePid(int pid); + bool SetTeletextPid(int pid); + + void trickSeek(int ratio); + bool SetSpeed(int speed); + bool SetSlow(int slow); + bool GetSpeed(int &speed) const; + bool GetPosition(int &position, int &duration); + void GetPts(uint64_t &pts); + int GetAPid(void); + int GetVPid(void); + int GetSubtitlePid(void); + bool SetPosition(int position, bool absolute = false); + void FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language); + void FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language); + void FindAllTeletextsubtitlePids(int *pids, unsigned int *numpidt, std::string *tlanguage, int *mags, int *pages); + void RequestAbort(void); + void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language); + bool SelectSubtitles(int pid); + uint64_t GetReadCount(void); + void GetChapters(std::vector &positions, std::vector &titles); + void GetMetadata(std::vector &keys, std::vector &values); + AVFormatContext *GetAVFormatContext(); + void ReleaseAVFormatContext(); + + // + ~cPlayback(); + void getMeta(); +}; + +#endif + diff --git a/libarmbox/pwrmngr.cpp b/libarmbox/pwrmngr.cpp new file mode 100644 index 0000000..b5ab30a --- /dev/null +++ b/libarmbox/pwrmngr.cpp @@ -0,0 +1,102 @@ +#include + +#include "pwrmngr.h" +#include "lt_debug.h" +#include +#include +#include +#include +#include + +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, this, args) +void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); } +void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); } +void cCpuFreqManager::Reset(void) { lt_debug("%s\n", __FUNCTION__); } +/* those function dummies return true or "harmless" values */ +bool cCpuFreqManager::SetDelta(unsigned long) { lt_debug("%s\n", __FUNCTION__); return true; } +#if HAVE_SPARK_HARDWARE || HAVE_DUCKBOX_HARDWARE +unsigned long cCpuFreqManager::GetCpuFreq(void) { + int freq = 0; + if (FILE *pll0 = fopen("/proc/cpu_frequ/pll0_ndiv_mdiv", "r")) { + char buffer[120]; + while(fgets(buffer, sizeof(buffer), pll0)) { + if (1 == sscanf(buffer, "SH4 = %d MHZ", &freq)) + break; + } + fclose(pll0); + return 1000 * 1000 * (unsigned long) freq; + } + return 0; +} +#else +unsigned long cCpuFreqManager::GetCpuFreq(void) { lt_debug("%s\n", __FUNCTION__); return 0; } +#endif +unsigned long cCpuFreqManager::GetDelta(void) { lt_debug("%s\n", __FUNCTION__); return 0; } +// +cCpuFreqManager::cCpuFreqManager(void) { lt_debug("%s\n", __FUNCTION__); } + +bool cPowerManager::SetState(PWR_STATE) { lt_debug("%s\n", __FUNCTION__); return true; } + +bool cPowerManager::Open(void) { lt_debug("%s\n", __FUNCTION__); return true; } +void cPowerManager::Close(void) { lt_debug("%s\n", __FUNCTION__); } +// +bool cPowerManager::SetStandby(bool Active, bool Passive) +{ + lt_debug("%s(%d, %d)\n", __FUNCTION__, Active, Passive); + return true; +} + +bool cCpuFreqManager::SetCpuFreq(unsigned long f) +{ +#if HAVE_SPARK_HARDWARE || HAVE_DUCKBOX_HARDWARE + if (f) { + FILE *pll0 = fopen ("/proc/cpu_frequ/pll0_ndiv_mdiv", "w"); + if (pll0) { + f /= 1000000; + fprintf(pll0, "%lu\n", (f/10 << 8) | 3); + fclose (pll0); + return false; + } + } +#else + /* actually SetCpuFreq is used to determine if the system is in standby + this is an "elegant" hack, because: + * during a recording, cpu freq is kept "high", even if the box is sent to standby + * the "SetStandby" call is made even if a recording is running + On the TD, setting standby disables the frontend, so we must not do it + if a recording is running. + For now, the values in neutrino are hardcoded: + * f == 0 => max => not standby + * f == 50000000 => min => standby + */ + lt_debug("%s(%lu) => set standby = %s\n", __FUNCTION__, f, f?"true":"false"); +#if 0 + int fd = open("/dev/stb/tdsystem", O_RDONLY); + if (fd < 0) + { + perror("open tdsystem"); + return false; + } + if (f) + { + ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ + ioctl(fd, IOC_AVS_STANDBY_ENTER); + } + else + { + ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ + ioctl(fd, IOC_AVS_STANDBY_LEAVE); + /* unmute will be done by cAudio::do_mute(). Ugly, but prevents pops */ + // ioctl(fd, IOC_AVS_SET_VOLUME, 0); /* max gain */ + } + + close(fd); +#endif +#endif + return true; +} + +// +cPowerManager::cPowerManager(void) { lt_debug("%s\n", __FUNCTION__); } +cPowerManager::~cPowerManager() { lt_debug("%s\n", __FUNCTION__); } + diff --git a/libarmbox/pwrmngr.h b/libarmbox/pwrmngr.h new file mode 100644 index 0000000..55dc984 --- /dev/null +++ b/libarmbox/pwrmngr.h @@ -0,0 +1,53 @@ +#ifndef __PWRMNGR_H__ +#define __PWRMNGR_H__ + +// -- cCpuFreqManager ---------------------------------------------------------- + +class cCpuFreqManager { +private: + unsigned long startCpuFreq; + unsigned long delta; +public: + void Up(void); + void Down(void); + void Reset(void); + // + bool SetCpuFreq(unsigned long CpuFreq); + bool SetDelta(unsigned long Delta); + unsigned long GetCpuFreq(void); + unsigned long GetDelta(void); + // + cCpuFreqManager(void); + +}; + +// -- cPowerManageger ---------------------------------------------------------- + +typedef enum +{ + PWR_INIT = 1, + PWR_FULL_ACTIVE, /* all devices/clocks up */ + PWR_ACTIVE_STANDBY, + PWR_PASSIVE_STANDBY, + PWR_INVALID +} PWR_STATE; + +class cPowerManager { +private: + bool init; + bool opened; + PWR_STATE powerState; + // + static void ApplicationCallback(void *, void *, signed long, void *, void *) {} + bool SetState(PWR_STATE PowerState); +public: + bool Open(void); + void Close(void); + // + bool SetStandby(bool Active, bool Passive); + // + cPowerManager(void); + virtual ~cPowerManager(); +}; + +#endif // __PWRMNGR_H__ diff --git a/libarmbox/record.cpp b/libarmbox/record.cpp new file mode 100644 index 0000000..cae962b --- /dev/null +++ b/libarmbox/record.cpp @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "record_lib.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_RECORD, this, args) + +/* helper functions to call the cpp thread loops */ +void *execute_record_thread(void *c) +{ + cRecord *obj = (cRecord *)c; + obj->RecordThread(); + return NULL; +} + +void *execute_writer_thread(void *c) +{ + cRecord *obj = (cRecord *)c; + obj->WriterThread(); + return NULL; +} + +cRecord::cRecord(int num, int bs_dmx, int bs) +{ + lt_info("%s %d\n", __func__, num); + dmx = NULL; + record_thread_running = false; + file_fd = -1; + exit_flag = RECORD_STOPPED; + dmx_num = num; + bufsize = bs; + bufsize_dmx = bs_dmx; + failureCallback = NULL; + failureData = NULL; +} + +cRecord::~cRecord() +{ + lt_info("%s: calling ::Stop()\n", __func__); + Stop(); + lt_info("%s: end\n", __func__); +} + +bool cRecord::Open(void) +{ + lt_info("%s\n", __func__); + exit_flag = RECORD_STOPPED; + return true; +} + +#if 0 +// unused +void cRecord::Close(void) +{ + lt_info("%s: \n", __func__); +} +#endif + +bool cRecord::Start(int fd, unsigned short vpid, unsigned short *apids, int numpids, uint64_t) +{ + lt_info("%s: fd %d, vpid 0x%03x\n", __func__, fd, vpid); + int i; + + if (!dmx) + dmx = new cDemux(dmx_num); + + dmx->Open(DMX_TP_CHANNEL, NULL, bufsize_dmx); + dmx->pesFilter(vpid); + + for (i = 0; i < numpids; i++) + dmx->addPid(apids[i]); + + file_fd = fd; + exit_flag = RECORD_RUNNING; + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + + i = pthread_create(&record_thread, 0, execute_record_thread, this); + if (i != 0) + { + exit_flag = RECORD_FAILED_READ; + errno = i; + lt_info("%s: error creating thread! (%m)\n", __func__); + delete dmx; + dmx = NULL; + return false; + } + record_thread_running = true; + return true; +} + +bool cRecord::Stop(void) +{ + lt_info("%s\n", __func__); + + if (exit_flag != RECORD_RUNNING) + lt_info("%s: status not RUNNING? (%d)\n", __func__, exit_flag); + + exit_flag = RECORD_STOPPED; + if (record_thread_running) + pthread_join(record_thread, NULL); + record_thread_running = false; + + /* We should probably do that from the destructor... */ + if (!dmx) + lt_info("%s: dmx == NULL?\n", __func__); + else + delete dmx; + dmx = NULL; + + if (file_fd != -1) + close(file_fd); + else + lt_info("%s: file_fd not open??\n", __func__); + file_fd = -1; + return true; +} + +bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int numapids) +{ + std::vector pids; + int j; + bool found; + unsigned short pid; + lt_info("%s\n", __func__); + if (!dmx) { + lt_info("%s: DMX = NULL\n", __func__); + return false; + } + pids = dmx->getPesPids(); + /* the first PID is the video pid, so start with the second PID... */ + for (std::vector::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) { + found = false; + pid = (*i).pid; + for (j = 0; j < numapids; j++) { + if (pid == apids[j]) { + found = true; + break; + } + } + if (!found) + dmx->removePid(pid); + } + for (j = 0; j < numapids; j++) { + found = false; + for (std::vector::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) { + if ((*i).pid == apids[j]) { + found = true; + break; + } + } + if (!found) + dmx->addPid(apids[j]); + } + return true; +} + +bool cRecord::AddPid(unsigned short pid) +{ + std::vector pids; + lt_info("%s: \n", __func__); + if (!dmx) { + lt_info("%s: DMX = NULL\n", __func__); + return false; + } + pids = dmx->getPesPids(); + for (std::vector::const_iterator i = pids.begin(); i != pids.end(); ++i) { + if ((*i).pid == pid) + return true; /* or is it an error to try to add the same PID twice? */ + } + return dmx->addPid(pid); +} + +void cRecord::WriterThread() +{ + char threadname[17]; + strncpy(threadname, "WriterThread", sizeof(threadname)); + threadname[16] = 0; + prctl (PR_SET_NAME, (unsigned long)&threadname); + unsigned int chunk = 0; + while (!sem_wait(&sem)) { + if (!io_len[chunk]) // empty, assume end of recording + return; + unsigned char *p_buf = io_buf[chunk]; + size_t p_len = io_len[chunk]; + while (p_len) { + ssize_t written = write(file_fd, p_buf, p_len); + if (written < 0) + break; + p_len -= written; + p_buf += written; + } + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + chunk++; + chunk %= RECORD_WRITER_CHUNKS; + } +} + +void cRecord::RecordThread() +{ + lt_info("%s: begin\n", __func__); + char threadname[17]; + strncpy(threadname, "RecordThread", sizeof(threadname)); + threadname[16] = 0; + prctl (PR_SET_NAME, (unsigned long)&threadname); + int readsize = bufsize/16; + int buf_pos = 0; + int count = 0; + int queued = 0; + uint8_t *buf; + struct aiocb a; + + buf = (uint8_t *)malloc(bufsize); + lt_info("BUFSIZE=0x%x READSIZE=0x%x\n", bufsize, readsize); + if (!buf) + { + exit_flag = RECORD_FAILED_MEMORY; + lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__); + if (failureCallback) + failureCallback(failureData); + lt_info("%s: end\n", __func__); + pthread_exit(NULL); + } + + int val = fcntl(file_fd, F_GETFL); + if (fcntl(file_fd, F_SETFL, val|O_APPEND)) + lt_info("%s: O_APPEND? (%m)\n", __func__); + + memset(&a, 0, sizeof(a)); + a.aio_fildes = file_fd; + a.aio_sigevent.sigev_notify = SIGEV_NONE; + + dmx->Start(); + int overflow_count = 0; + bool overflow = false; + int r = 0; + while (exit_flag == RECORD_RUNNING) + { + if (buf_pos < bufsize) + { + if (overflow_count) { + lt_info("%s: Overflow cleared after %d iterations\n", __func__, overflow_count); + overflow_count = 0; + } + int toread = bufsize - buf_pos; + if (toread > readsize) + toread = readsize; + ssize_t s = dmx->Read(buf + buf_pos, toread, 50); + lt_debug("%s: buf_pos %6d s %6d / %6d\n", __func__, buf_pos, (int)s, bufsize - buf_pos); + if (s < 0) + { + if (errno != EAGAIN && (errno != EOVERFLOW || !overflow)) + { + lt_info("%s: read failed: %m\n", __func__); + exit_flag = RECORD_FAILED_READ; + break; + } + } + else + { + overflow = false; + buf_pos += s; + if (count > 100) + { + if (buf_pos < bufsize / 2) + continue; + } + else + { + count += 1; + } + } + } + else + { + if (!overflow) + overflow_count = 0; + overflow = true; + if (!(overflow_count % 10)) + lt_info("%s: buffer full! Overflow? (%d)\n", __func__, ++overflow_count); + } + r = aio_error(&a); + if (r == EINPROGRESS) + { + lt_debug("%s: aio in progress, free: %d\n", __func__, bufsize - buf_pos); + continue; + } + // not calling aio_return causes a memory leak --martii + r = aio_return(&a); + if (r < 0) + { + exit_flag = RECORD_FAILED_FILE; + lt_debug("%s: aio_return = %d (%m)\n", __func__, r); + break; + } + else + lt_debug("%s: aio_return = %d, free: %d\n", __func__, r, bufsize - buf_pos); + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + if (queued) + { + memmove(buf, buf + queued, buf_pos - queued); + buf_pos -= queued; + } + queued = buf_pos; + a.aio_buf = buf; + a.aio_nbytes = queued; + r = aio_write(&a); + if (r) + { + lt_info("%s: aio_write %d (%m)\n", __func__, r); + exit_flag = RECORD_FAILED_FILE; + break; + } + } + dmx->Stop(); + while (true) /* write out the unwritten buffer content */ + { + lt_debug("%s: run-out write, buf_pos %d\n", __func__, buf_pos); + r = aio_error(&a); + if (r == EINPROGRESS) + { + usleep(50000); + continue; + } + r = aio_return(&a); + if (r < 0) + { + exit_flag = RECORD_FAILED_FILE; + lt_info("%s: aio_result: %d (%m)\n", __func__, r); + break; + } + if (!queued) + break; + memmove(buf, buf + queued, buf_pos - queued); + buf_pos -= queued; + queued = buf_pos; + a.aio_buf = buf; + a.aio_nbytes = queued; + r = aio_write(&a); + } + free(buf); + +#if 0 + // TODO: do we need to notify neutrino about failing recording? + CEventServer eventServer; + eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); + stream2file_status2_t s; + s.status = exit_flag; + strncpy(s.filename,basename(myfilename),512); + s.filename[511] = '\0'; + strncpy(s.dir,dirname(myfilename),100); + s.dir[99] = '\0'; + eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); + printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); +#endif + + if ((exit_flag != RECORD_STOPPED) && failureCallback) + failureCallback(failureData); + lt_info("%s: end\n", __func__); + pthread_exit(NULL); +} + +int cRecord::GetStatus() +{ + return (exit_flag == RECORD_STOPPED) ? REC_STATUS_STOPPED : REC_STATUS_OK; +} + +void cRecord::ResetStatus() +{ + return; +} diff --git a/libarmbox/record_lib.h b/libarmbox/record_lib.h new file mode 100644 index 0000000..5ff453e --- /dev/null +++ b/libarmbox/record_lib.h @@ -0,0 +1,57 @@ +#ifndef __RECORD_TD_H +#define __RECORD_TD_H + +#include +#include +#include "dmx_lib.h" + +#define REC_STATUS_OK 0 +#define REC_STATUS_SLOW 1 +#define REC_STATUS_OVERFLOW 2 +#define REC_STATUS_STOPPED 4 + +typedef enum { + RECORD_RUNNING, + RECORD_STOPPED, + RECORD_FAILED_READ, /* failed to read from DMX */ + RECORD_FAILED_OVERFLOW, /* cannot write fast enough */ + RECORD_FAILED_FILE, /* cannot write to file */ + RECORD_FAILED_MEMORY /* out of memory */ +} record_state_t; + +class cRecord +{ + private: + int file_fd; + int dmx_num; + cDemux *dmx; + pthread_t record_thread; + bool record_thread_running; + record_state_t exit_flag; + int state; + int bufsize; + int bufsize_dmx; + void (*failureCallback)(void *); + void *failureData; + + sem_t sem; +#define RECORD_WRITER_CHUNKS 16 + unsigned char *io_buf[RECORD_WRITER_CHUNKS]; + size_t io_len[RECORD_WRITER_CHUNKS]; + public: + cRecord(int num = 0, int bs_dmx = 2048 * 1024, int bs = 4096 * 1024); + void setFailureCallback(void (*f)(void *), void *d) { failureCallback = f; failureData = d; } + ~cRecord(); + + bool Open(); + bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids, uint64_t ch = 0); + bool Stop(void); + bool AddPid(unsigned short pid); + int GetStatus(); + void ResetStatus(); + bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids); + + void RecordThread(); + void WriterThread(); +}; +#endif diff --git a/libarmbox/video.cpp b/libarmbox/video.cpp new file mode 100644 index 0000000..41855db --- /dev/null +++ b/libarmbox/video.cpp @@ -0,0 +1,1053 @@ +/* + * (C) 2002-2003 Andreas Oberritter + * (C) 2010-2013, 2015 Stefan Seyfried + * + * 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; 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include "video_lib.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args) +#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, args) + +#define fop(cmd, args...) ({ \ + int _r; \ + if (fd >= 0) { \ + if ((_r = ::cmd(fd, args)) < 0) \ + lt_info(#cmd"(fd, "#args")\n"); \ + else \ + lt_debug(#cmd"(fd, "#args")\n");\ + } \ + else { _r = fd; } \ + _r; \ +}) + +cVideo * videoDecoder = NULL; +cVideo * pipDecoder = NULL; + +int system_rev = 0; + +static bool hdmi_enabled = true; +static bool stillpicture = false; + +static const char *VDEV[] = { + "/dev/dvb/adapter0/video0", + "/dev/dvb/adapter0/video1" +}; +static const char *VMPEG_aspect[] = { + "/proc/stb/vmpeg/0/aspect", + "/proc/stb/vmpeg/1/aspect" +}; + +static const char *VMPEG_xres[] = { + "/proc/stb/vmpeg/0/xres", + "/proc/stb/vmpeg/1/xres" +}; + +static const char *VMPEG_yres[] = { + "/proc/stb/vmpeg/0/yres", + "/proc/stb/vmpeg/1/yres" +}; + +static const char *VMPEG_dst_all[] = { + "/proc/stb/vmpeg/0/dst_all", + "/proc/stb/vmpeg/1/dst_all" +}; + +static const char *VMPEG_framerate[] = { + "/proc/stb/vmpeg/0/framerate", + "/proc/stb/vmpeg/1/framerate" +}; + + +#define VIDEO_STREAMTYPE_MPEG2 0 +#define VIDEO_STREAMTYPE_MPEG4_H264 1 +#define VIDEO_STREAMTYPE_VC1 3 +#define VIDEO_STREAMTYPE_MPEG4_Part2 4 +#define VIDEO_STREAMTYPE_VC1_SM 5 +#define VIDEO_STREAMTYPE_MPEG1 6 +#define VIDEO_STREAMTYPE_H265_HEVC 7 +#define VIDEO_STREAMTYPE_AVS 16 + + +static int proc_put(const char *path, const char *value, const int len) +{ + int ret, ret2; + int pfd = open(path, O_WRONLY); + if (pfd < 0) + return pfd; + ret = write(pfd, value, len); + ret2 = close(pfd); + if (ret2 < 0) + return ret2; + return ret; +} + +static int proc_get(const char *path, char *value, const int len) +{ + int ret, ret2; + int pfd = open(path, O_RDONLY); + if (pfd < 0) + return pfd; + ret = read(pfd, value, len); + value[len-1] = '\0'; /* make sure string is terminated */ + while (ret > 0 && isspace(value[ret-1])) + value[--ret] = '\0'; /* remove trailing whitespace */ + ret2 = close(pfd); + if (ret2 < 0) + return ret2; + return ret; +} + +static unsigned int proc_get_hex(const char *path) +{ + unsigned int n, ret = 0; + char buf[16]; + n = proc_get(path, buf, 16); + if (n > 0) + sscanf(buf, "%x", &ret); + return ret; +} + +static int hdmi_out(bool enable) +{ + int ret = -1; + return ret; +} + + +cVideo::cVideo(int, void *, void *, unsigned int unit) +{ + lt_debug("%s unit %u\n", __func__, unit); + + brightness = -1; + contrast = -1; + saturation = -1; + hue = -1; + + scartvoltage = -1; + video_standby = 0; + if (unit > 1) { + lt_info("%s: unit %d out of range, setting to 0\n", __func__, unit); + devnum = 0; + } else + devnum = unit; + fd = -1; + openDevice(); +} + +cVideo::~cVideo(void) +{ + closeDevice(); +} + +void cVideo::openDevice(void) +{ + int n = 0; + lt_debug("#%d: %s\n", devnum, __func__); + /* todo: this fd checking is racy, should be protected by a lock */ + if (fd != -1) /* already open */ + return; +retry: + if ((fd = open(VDEV[devnum], O_RDWR|O_CLOEXEC)) < 0) + { + if (errno == EBUSY) + { + /* sometimes we get busy quickly after close() */ + usleep(50000); + if (++n < 10) + goto retry; + } + lt_info("#%d: %s cannot open %s: %m, retries %d\n", devnum, __func__, VDEV[devnum], n); + } + playstate = VIDEO_STOPPED; +} + +void cVideo::closeDevice(void) +{ + lt_debug("%s\n", __func__); + /* looks like sometimes close is unhappy about non-empty buffers */ + Start(); + if (fd >= 0) + close(fd); + fd = -1; + playstate = VIDEO_STOPPED; +} + +int cVideo::setAspectRatio(int aspect, int mode) +{ + static const char *a[] = { "n/a", "4:3", "14:9", "16:9" }; + static const char *m[] = { "panscan", "letterbox", "bestfit", "nonlinear", "(unset)" }; + int n; + lt_debug("%s: a:%d m:%d %s\n", __func__, aspect, mode, m[(mode < 0||mode > 3) ? 4 : mode]); + + if (aspect > 3 || aspect == 0) + lt_info("%s: invalid aspect: %d\n", __func__, aspect); + else if (aspect > 0) /* -1 == don't set */ + { + lt_debug("%s: /proc/stb/video/aspect -> %s\n", __func__, a[aspect]); + n = proc_put("/proc/stb/video/aspect", a[aspect], strlen(a[aspect])); + if (n < 0) + lt_info("%s: proc_put /proc/stb/video/aspect (%m)\n", __func__); + } + + if (mode == -1) + return 0; + + lt_debug("%s: /proc/stb/video/policy -> %s\n", __func__, m[mode]); + n = proc_put("/proc/stb/video/policy", m[mode], strlen(m[mode])); + if (n < 0) + return 1; + return 0; +} + +int cVideo::getAspectRatio(void) +{ + video_size_t s; + if (fd == -1) + { + /* in movieplayer mode, fd is not opened -> fall back to procfs */ + int n = proc_get_hex(VMPEG_aspect[devnum]); + return n * 2 + 1; + } + if (fop(ioctl, VIDEO_GET_SIZE, &s) < 0) + { + lt_info("%s: VIDEO_GET_SIZE %m\n", __func__); + return -1; + } + lt_debug("#%d: %s: %d\n", devnum, __func__, s.aspect_ratio); + return s.aspect_ratio * 2 + 1; +} + +int cVideo::setCroppingMode(int /*vidDispMode_t format*/) +{ + return 0; +#if 0 + croppingMode = format; + const char *format_string[] = { "norm", "letterbox", "unknown", "mode_1_2", "mode_1_4", "mode_2x", "scale", "disexp" }; + const char *f; + if (format >= VID_DISPMODE_NORM && format <= VID_DISPMODE_DISEXP) + f = format_string[format]; + else + f = "ILLEGAL format!"; + lt_debug("%s(%d) => %s\n", __FUNCTION__, format, f); + return fop(ioctl, MPEG_VID_SET_DISPMODE, format); +#endif +} + +int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) +{ + lt_debug("#%d: %s playstate=%d\n", devnum, __func__, playstate); +#if 0 + if (playstate == VIDEO_PLAYING) + return 0; + if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ + fop(ioctl, MPEG_VID_CONTINUE); +#endif + playstate = VIDEO_PLAYING; + fop(ioctl, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); + int res = fop(ioctl, VIDEO_PLAY); + if (brightness > -1) { + SetControl(VIDEO_CONTROL_BRIGHTNESS, brightness); + brightness = -1; + } + if (contrast > -1) { + SetControl(VIDEO_CONTROL_CONTRAST, contrast); + contrast = -1; + } + if (saturation > -1) { + SetControl(VIDEO_CONTROL_SATURATION, saturation); + saturation = -1; + } + if (hue > -1) { + SetControl(VIDEO_CONTROL_HUE, hue); + hue = -1; + } + return res; +} + +int cVideo::Stop(bool blank) +{ + lt_debug("#%d: %s(%d)\n", devnum, __func__, blank); + if (stillpicture) + { + lt_debug("%s: stillpicture == true\n", __func__); + return -1; + } + playstate = blank ? VIDEO_STOPPED : VIDEO_FREEZED; + return fop(ioctl, VIDEO_STOP, blank ? 1 : 0); +} + +int cVideo::setBlank(int) +{ + fop(ioctl, VIDEO_PLAY); + fop(ioctl, VIDEO_CONTINUE); + video_still_picture sp = { NULL, 0 }; + fop(ioctl, VIDEO_STILLPICTURE, &sp); + return Stop(1); +} + +int cVideo::GetVideoSystem() +{ + lt_debug("%s\n", __func__); + char current[32]; + static const char *modes[] = { + "pal", // VIDEO_STD_NTSC + "pal", // VIDEO_STD_SECAM + "pal", // VIDEO_STD_PAL + "480p", // VIDEO_STD_480P + "576p50", // VIDEO_STD_576P + "720p60", // VIDEO_STD_720P60 + "1080i60", // VIDEO_STD_1080I60 + "720p50", // VIDEO_STD_720P50 + "1080i50", // VIDEO_STD_1080I50 + "1080p30", // VIDEO_STD_1080P30 + "1080p24", // VIDEO_STD_1080P24 + "1080p25", // VIDEO_STD_1080P25 + "1080p50", // VIDEO_STD_1080P50 + "1080p60", // VIDEO_STD_1080P60 + "1080p2397", // VIDEO_STD_1080P2397 + "1080p2997", // VIDEO_STD_1080P2997 + "720p50" // VIDEO_STD_AUTO + }; + + int ret = proc_get("/proc/stb/video/videomode", current, 32); + for (int i=0; i VIDEO_STD_MAX) + { + lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", __func__, video_system, VIDEO_STD_MAX); + return -1; + } + int ret = proc_get("/proc/stb/video/videomode", current, 32); + if (strcmp(current, modes[video_system]) == 0) + { + lt_info("%s: video_system %d (%s) already set, skipping\n", __func__, video_system, current); + return 0; + } + lt_info("%s: old: '%s' new: '%s'\n", __func__, current, modes[video_system]); + bool stopped = false; + if (playstate == VIDEO_PLAYING) + { + lt_info("%s: playstate == VIDEO_PLAYING, stopping video\n", __func__); + Stop(); + stopped = true; + } + hdmi_out(false); + ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])); + hdmi_out(true); + if (stopped) + Start(); + + return ret; +} + +int cVideo::getPlayState(void) +{ + return playstate; +} + +void cVideo::SetVideoMode(analog_mode_t mode) +{ + lt_debug("#%d: %s(%d)\n", devnum, __func__, mode); + if (!(mode & ANALOG_SCART_MASK)) + { + lt_debug("%s: non-SCART mode ignored\n", __func__); + return; + } + const char *m; + switch(mode) + { + case ANALOG_SD_YPRPB_SCART: + m = "yuv"; + break; + case ANALOG_SD_RGB_SCART: + m = "rgb"; + break; + default: + lt_info("%s unknown mode %d\n", __func__, mode); + m = "rgb"; + break; /* default to rgb */ + } + proc_put("/proc/stb/avs/0/colorformat", m, strlen(m)); +} + +void cVideo::ShowPicture(const char * fname, const char *_destname) +{ + lt_debug("%s(%s)\n", __func__, fname); + static const unsigned char pes_header[] = { 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x00, 0x00 }; + static const unsigned char seq_end[] = { 0x00, 0x00, 0x01, 0xB7 }; + char destname[512]; + char cmd[512]; + char *p; + int mfd; + struct stat st, st2; + if (video_standby) + { + /* does not work and the driver does not seem to like it */ + lt_info("%s: video_standby == true\n", __func__); + return; + } + const char *lastDot = strrchr(fname, '.'); + if (lastDot && !strcasecmp(lastDot + 1, "m2v")) + strncpy(destname, fname, sizeof(destname)); + else { + if (_destname) + strncpy(destname, _destname, sizeof(destname)); + else { + strcpy(destname, "/tmp/cache"); + if (stat(fname, &st2)) + { + lt_info("%s: could not stat %s (%m)\n", __func__, fname); + return; + } + mkdir(destname, 0755); + /* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg): + /var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v + build that filename first... + TODO: this could cause name clashes, use a hashing function instead... */ + strcat(destname, fname); + p = &destname[strlen("/tmp/cache/")]; + while ((p = strchr(p, '/')) != NULL) + *p = '.'; + strcat(destname, ".m2v"); + } + /* ...then check if it exists already... */ + if (stat(destname, &st) || (st.st_mtime != st2.st_mtime) || (st.st_size == 0)) + { + struct utimbuf u; + u.actime = time(NULL); + u.modtime = st2.st_mtime; + /* it does not exist or has a different date, so call ffmpeg... */ + sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 1280x720 -aspect 16:9 '%s' = 0) + { + stillpicture = true; + + if (ioctl(fd, VIDEO_SET_FORMAT, VIDEO_FORMAT_16_9) < 0) + lt_info("%s: VIDEO_SET_FORMAT failed (%m)\n", __func__); + bool seq_end_avail = false; + off_t pos=0; + unsigned char *iframe = (unsigned char *)malloc((st.st_size < 8192) ? 8192 : st.st_size); + if (! iframe) + { + lt_info("%s: malloc failed (%m)\n", __func__); + goto out; + } + read(mfd, iframe, st.st_size); + ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY); + ioctl(fd, VIDEO_PLAY); + ioctl(fd, VIDEO_CONTINUE); + ioctl(fd, VIDEO_CLEAR_BUFFER); + while (pos <= (st.st_size-4) && !(seq_end_avail = (!iframe[pos] && !iframe[pos+1] && iframe[pos+2] == 1 && iframe[pos+3] == 0xB7))) + ++pos; + + if ((iframe[3] >> 4) != 0xE) // no pes header + write(fd, pes_header, sizeof(pes_header)); + write(fd, iframe, st.st_size); + if (!seq_end_avail) + write(fd, seq_end, sizeof(seq_end)); + memset(iframe, 0, 8192); + write(fd, iframe, 8192); + ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); + free(iframe); + } + out: + close(mfd); + return; +} + +void cVideo::StopPicture() +{ + lt_debug("%s\n", __func__); + stillpicture = false; + Stop(1); +} + +void cVideo::Standby(unsigned int bOn) +{ + lt_debug("%s(%d)\n", __func__, bOn); + if (bOn) + { + closeDevice(); + hdmi_out(false); + } + else + { + /* only enable HDMI output when coming from standby, not on + * start. I have no idea why, but enabling it on startup leads + * to strange locking problems of the framebuffer driver :-( */ + if (!hdmi_enabled) + { + hdmi_out(true); + /* make sure the driver has time to settle. + * again - lame, but makes it work... */ + sleep(1); + } + openDevice(); + } + video_standby = bOn; +} + +int cVideo::getBlank(void) +{ + static unsigned int lastcount = 0; + unsigned int count = 0; + size_t n = 0; + ssize_t r; + char *line = NULL; + /* hack: the "mailbox" irq is not increasing if + * no audio or video is decoded... */ + FILE *f = fopen("/proc/interrupts", "r"); + if (! f) /* huh? */ + return 0; + while ((r = getline(&line, &n, f)) != -1) + { + if (r <= (ssize_t) strlen("mailbox")) /* should not happen... */ + continue; + line[r - 1] = 0; /* remove \n */ + if (!strcmp(&line[r - 1 - strlen("mailbox")], "mailbox")) + { + count = atoi(line + 5); + break; + } + } + free(line); + fclose(f); + int ret = (count == lastcount); /* no new decode -> return 1 */ + lt_debug("#%d: %s: %d (irq++: %d)\n", devnum, __func__, ret, count - lastcount); + lastcount = count; + return ret; +} + +/* this function is regularly called, checks if video parameters + changed and triggers appropriate actions */ +void cVideo::VideoParamWatchdog(void) +{ +#if 0 + static unsigned int _v_info = (unsigned int) -1; + unsigned int v_info; + if (fd == -1) + return; + ioctl(fd, MPEG_VID_GET_V_INFO_RAW, &v_info); + if (_v_info != v_info) + { + lt_debug("%s params changed. old: %08x new: %08x\n", __FUNCTION__, _v_info, v_info); + setAspectRatio(-1, -1); + } + _v_info = v_info; +#endif +} + +void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h, int startx, int starty, int endx, int endy) +{ + char buffer[64]; + int _x, _y, _w, _h; + /* the target "coordinates" seem to be in a PAL sized plane + * TODO: check this in the driver sources */ + int xres = 720; /* proc_get_hex("/proc/stb/vmpeg/0/xres") */ + int yres = 576; /* proc_get_hex("/proc/stb/vmpeg/0/yres") */ + lt_debug("#%d %s: x:%d y:%d w:%d h:%d ow:%d oh:%d\n", devnum, __func__, x, y, w, h, osd_w, osd_h); + if (x == -1 && y == -1 && w == -1 && h == -1) + { + _w = xres; + _h = yres; + _x = 0; + _y = 0; + } + else + { + // need to do some additional adjustments because osd border is handled by blitter + x += startx; + x *= endx - startx + 1; + y += starty; + y *= endy - starty + 1; + w *= endx - startx + 1; + h *= endy - starty + 1; + _x = x * xres / osd_w; + _w = w * xres / osd_w; + _y = y * yres / osd_h; + _h = h * yres / osd_h; + _x /= 1280; + _y /= 720; + _w /= 1280; + _h /= 720; + } + lt_debug("#%d %s: x:%d y:%d w:%d h:%d xr:%d yr:%d\n", devnum, __func__, _x, _y, _w, _h, xres, yres); + sprintf(buffer, "%x %x %x %x", _x, _y, _w, _h); + proc_put(VMPEG_dst_all[devnum], buffer, strlen(buffer)); +} + +static inline int rate2csapi(int rate) +{ + switch (rate) + { + case 23976: + return 0; + case 24000: + return 1; + case 25000: + return 2; + case 29976: + return 3; + case 30000: + return 4; + case 50000: + return 5; + case 50940: + return 6; + case 60000: + return 7; + default: + break; + } + return -1; +} + +void cVideo::getPictureInfo(int &width, int &height, int &rate) +{ + video_size_t s; + int r; + if (fd == -1) + { + /* in movieplayer mode, fd is not opened -> fall back to procfs */ + r = proc_get_hex(VMPEG_framerate[devnum]); + width = proc_get_hex(VMPEG_xres[devnum]); + height = proc_get_hex(VMPEG_yres[devnum]); + rate = rate2csapi(r); + return; + } + ioctl(fd, VIDEO_GET_SIZE, &s); + ioctl(fd, VIDEO_GET_FRAME_RATE, &r); + rate = rate2csapi(r); + height = s.h; + width = s.w; + lt_debug("#%d: %s: rate: %d, width: %d height: %d\n", devnum, __func__, rate, width, height); +} + +void cVideo::SetSyncMode(AVSYNC_TYPE mode) +{ + lt_debug("%s %d\n", __func__, mode); + /* + * { 0, LOCALE_OPTIONS_OFF }, + * { 1, LOCALE_OPTIONS_ON }, + * { 2, LOCALE_AUDIOMENU_AVSYNC_AM } + */ + const char *apply[] = { "disapply", "apply" }; + const char *clock[] = { "video", "audio" }; + const char *a = apply[mode > 0]; /* mode == 1 or mode == 2 -> "apply" */ + const char *c = clock[mode > 1]; /* mode == 2 -> "audio" */ + proc_put("/proc/stb/stream/policy/AV_SYNC", a, strlen(a)); + proc_put("/proc/stb/stream/policy/MASTER_CLOCK", c, strlen(c)); +}; + +int cVideo::SetStreamType(VIDEO_FORMAT type) +{ + static const char *VF[] = { + "VIDEO_FORMAT_MPEG2", + "VIDEO_FORMAT_MPEG4", + "VIDEO_FORMAT_VC1", + "VIDEO_FORMAT_JPEG", + "VIDEO_FORMAT_GIF", + "VIDEO_FORMAT_PNG" + }; + int t; + lt_debug("#%d: %s type=%s\n", devnum, __func__, VF[type]); + + switch (type) + { + case VIDEO_FORMAT_MPEG4_H264: + t = VIDEO_STREAMTYPE_MPEG4_H264; + break; + case VIDEO_FORMAT_MPEG4_H265: + t = VIDEO_STREAMTYPE_H265_HEVC; + break; + case VIDEO_FORMAT_AVS: + t = VIDEO_STREAMTYPE_AVS; + break; + case VIDEO_FORMAT_VC1: + t = VIDEO_STREAMTYPE_VC1; + break; + case VIDEO_FORMAT_MPEG2: + default: + t = VIDEO_STREAMTYPE_MPEG2; + break; + } + + if (ioctl(fd, VIDEO_SET_STREAMTYPE, t) < 0) + lt_info("%s VIDEO_SET_STREAMTYPE(%d) failed: %m\n", __func__, t); + return 0; +} + +int64_t cVideo::GetPTS(void) +{ + int64_t pts = 0; + if (ioctl(fd, VIDEO_GET_PTS, &pts) < 0) + lt_info("%s: GET_PTS failed (%m)\n", __func__); + return pts; +} + +void cVideo::SetDemux(cDemux *) +{ + lt_debug("#%d %s not implemented yet\n", devnum, __func__); +} + +void cVideo::SetControl(int control, int value) { + const char *p = NULL; + switch (control) { + case VIDEO_CONTROL_BRIGHTNESS: + brightness = value; + p = "/proc/stb/video/plane/psi_brightness"; + break; + case VIDEO_CONTROL_CONTRAST: + contrast = value; + p = "/proc/stb/video/plane/psi_contrast"; + break; + case VIDEO_CONTROL_SATURATION: + saturation = value; + p = "/proc/stb/video/plane/psi_saturation"; + break; + case VIDEO_CONTROL_HUE: + hue = value; + p = "/proc/stb/video/plane/psi_tint"; + break; + } + if (p) { + char buf[20]; + int len = snprintf(buf, sizeof(buf), "%d", value); + if (len < (int) sizeof(buf)) + proc_put(p, buf, len); + } +} + +void cVideo::SetColorFormat(COLOR_FORMAT color_format) { + const char *p = NULL; + switch(color_format) { + case COLORFORMAT_RGB: + p = "rgb"; + break; + case COLORFORMAT_YUV: + p = "yuv"; + break; + case COLORFORMAT_CVBS: + p = "cvbs"; + break; + case COLORFORMAT_SVIDEO: + p = "svideo"; + break; + case COLORFORMAT_HDMI_RGB: + p = "hdmi_rgb"; + break; + case COLORFORMAT_HDMI_YCBCR444: + p = "hdmi_yuv"; + break; + case COLORFORMAT_HDMI_YCBCR422: + p = "hdmi_422"; + break; + } + if (p) + proc_put("/proc/stb/avs/0/colorformat", p, strlen(p)); +} + +/* get an image of the video screen + * this code is inspired by dreambox AIO-grab, + * git://schwerkraft.elitedvb.net/aio-grab/aio-grab.git + * and the patches for STi support from + * https://github.com/Schischu/STLinux.BSP-Duckbox.git */ +/* static lookup tables for faster yuv2rgb conversion */ +static const uint32_t yuv2rgbtable_y[256] = { + 0xFFED5EA0, 0xFFEE88B6, 0xFFEFB2CC, 0xFFF0DCE2, 0xFFF206F8, 0xFFF3310E, 0xFFF45B24, 0xFFF5853A, + 0xFFF6AF50, 0xFFF7D966, 0xFFF9037C, 0xFFFA2D92, 0xFFFB57A8, 0xFFFC81BE, 0xFFFDABD4, 0xFFFED5EA, + 0x00000000, 0x00012A16, 0x0002542C, 0x00037E42, 0x0004A858, 0x0005D26E, 0x0006FC84, 0x0008269A, + 0x000950B0, 0x000A7AC6, 0x000BA4DC, 0x000CCEF2, 0x000DF908, 0x000F231E, 0x00104D34, 0x0011774A, + 0x0012A160, 0x0013CB76, 0x0014F58C, 0x00161FA2, 0x001749B8, 0x001873CE, 0x00199DE4, 0x001AC7FA, + 0x001BF210, 0x001D1C26, 0x001E463C, 0x001F7052, 0x00209A68, 0x0021C47E, 0x0022EE94, 0x002418AA, + 0x002542C0, 0x00266CD6, 0x002796EC, 0x0028C102, 0x0029EB18, 0x002B152E, 0x002C3F44, 0x002D695A, + 0x002E9370, 0x002FBD86, 0x0030E79C, 0x003211B2, 0x00333BC8, 0x003465DE, 0x00358FF4, 0x0036BA0A, + 0x0037E420, 0x00390E36, 0x003A384C, 0x003B6262, 0x003C8C78, 0x003DB68E, 0x003EE0A4, 0x00400ABA, + 0x004134D0, 0x00425EE6, 0x004388FC, 0x0044B312, 0x0045DD28, 0x0047073E, 0x00483154, 0x00495B6A, + 0x004A8580, 0x004BAF96, 0x004CD9AC, 0x004E03C2, 0x004F2DD8, 0x005057EE, 0x00518204, 0x0052AC1A, + 0x0053D630, 0x00550046, 0x00562A5C, 0x00575472, 0x00587E88, 0x0059A89E, 0x005AD2B4, 0x005BFCCA, + 0x005D26E0, 0x005E50F6, 0x005F7B0C, 0x0060A522, 0x0061CF38, 0x0062F94E, 0x00642364, 0x00654D7A, + 0x00667790, 0x0067A1A6, 0x0068CBBC, 0x0069F5D2, 0x006B1FE8, 0x006C49FE, 0x006D7414, 0x006E9E2A, + 0x006FC840, 0x0070F256, 0x00721C6C, 0x00734682, 0x00747098, 0x00759AAE, 0x0076C4C4, 0x0077EEDA, + 0x007918F0, 0x007A4306, 0x007B6D1C, 0x007C9732, 0x007DC148, 0x007EEB5E, 0x00801574, 0x00813F8A, + 0x008269A0, 0x008393B6, 0x0084BDCC, 0x0085E7E2, 0x008711F8, 0x00883C0E, 0x00896624, 0x008A903A, + 0x008BBA50, 0x008CE466, 0x008E0E7C, 0x008F3892, 0x009062A8, 0x00918CBE, 0x0092B6D4, 0x0093E0EA, + 0x00950B00, 0x00963516, 0x00975F2C, 0x00988942, 0x0099B358, 0x009ADD6E, 0x009C0784, 0x009D319A, + 0x009E5BB0, 0x009F85C6, 0x00A0AFDC, 0x00A1D9F2, 0x00A30408, 0x00A42E1E, 0x00A55834, 0x00A6824A, + 0x00A7AC60, 0x00A8D676, 0x00AA008C, 0x00AB2AA2, 0x00AC54B8, 0x00AD7ECE, 0x00AEA8E4, 0x00AFD2FA, + 0x00B0FD10, 0x00B22726, 0x00B3513C, 0x00B47B52, 0x00B5A568, 0x00B6CF7E, 0x00B7F994, 0x00B923AA, + 0x00BA4DC0, 0x00BB77D6, 0x00BCA1EC, 0x00BDCC02, 0x00BEF618, 0x00C0202E, 0x00C14A44, 0x00C2745A, + 0x00C39E70, 0x00C4C886, 0x00C5F29C, 0x00C71CB2, 0x00C846C8, 0x00C970DE, 0x00CA9AF4, 0x00CBC50A, + 0x00CCEF20, 0x00CE1936, 0x00CF434C, 0x00D06D62, 0x00D19778, 0x00D2C18E, 0x00D3EBA4, 0x00D515BA, + 0x00D63FD0, 0x00D769E6, 0x00D893FC, 0x00D9BE12, 0x00DAE828, 0x00DC123E, 0x00DD3C54, 0x00DE666A, + 0x00DF9080, 0x00E0BA96, 0x00E1E4AC, 0x00E30EC2, 0x00E438D8, 0x00E562EE, 0x00E68D04, 0x00E7B71A, + 0x00E8E130, 0x00EA0B46, 0x00EB355C, 0x00EC5F72, 0x00ED8988, 0x00EEB39E, 0x00EFDDB4, 0x00F107CA, + 0x00F231E0, 0x00F35BF6, 0x00F4860C, 0x00F5B022, 0x00F6DA38, 0x00F8044E, 0x00F92E64, 0x00FA587A, + 0x00FB8290, 0x00FCACA6, 0x00FDD6BC, 0x00FF00D2, 0x01002AE8, 0x010154FE, 0x01027F14, 0x0103A92A, + 0x0104D340, 0x0105FD56, 0x0107276C, 0x01085182, 0x01097B98, 0x010AA5AE, 0x010BCFC4, 0x010CF9DA, + 0x010E23F0, 0x010F4E06, 0x0110781C, 0x0111A232, 0x0112CC48, 0x0113F65E, 0x01152074, 0x01164A8A +}; +static const uint32_t yuv2rgbtable_ru[256] = { + 0xFEFDA500, 0xFEFFA9B6, 0xFF01AE6C, 0xFF03B322, 0xFF05B7D8, 0xFF07BC8E, 0xFF09C144, 0xFF0BC5FA, + 0xFF0DCAB0, 0xFF0FCF66, 0xFF11D41C, 0xFF13D8D2, 0xFF15DD88, 0xFF17E23E, 0xFF19E6F4, 0xFF1BEBAA, + 0xFF1DF060, 0xFF1FF516, 0xFF21F9CC, 0xFF23FE82, 0xFF260338, 0xFF2807EE, 0xFF2A0CA4, 0xFF2C115A, + 0xFF2E1610, 0xFF301AC6, 0xFF321F7C, 0xFF342432, 0xFF3628E8, 0xFF382D9E, 0xFF3A3254, 0xFF3C370A, + 0xFF3E3BC0, 0xFF404076, 0xFF42452C, 0xFF4449E2, 0xFF464E98, 0xFF48534E, 0xFF4A5804, 0xFF4C5CBA, + 0xFF4E6170, 0xFF506626, 0xFF526ADC, 0xFF546F92, 0xFF567448, 0xFF5878FE, 0xFF5A7DB4, 0xFF5C826A, + 0xFF5E8720, 0xFF608BD6, 0xFF62908C, 0xFF649542, 0xFF6699F8, 0xFF689EAE, 0xFF6AA364, 0xFF6CA81A, + 0xFF6EACD0, 0xFF70B186, 0xFF72B63C, 0xFF74BAF2, 0xFF76BFA8, 0xFF78C45E, 0xFF7AC914, 0xFF7CCDCA, + 0xFF7ED280, 0xFF80D736, 0xFF82DBEC, 0xFF84E0A2, 0xFF86E558, 0xFF88EA0E, 0xFF8AEEC4, 0xFF8CF37A, + 0xFF8EF830, 0xFF90FCE6, 0xFF93019C, 0xFF950652, 0xFF970B08, 0xFF990FBE, 0xFF9B1474, 0xFF9D192A, + 0xFF9F1DE0, 0xFFA12296, 0xFFA3274C, 0xFFA52C02, 0xFFA730B8, 0xFFA9356E, 0xFFAB3A24, 0xFFAD3EDA, + 0xFFAF4390, 0xFFB14846, 0xFFB34CFC, 0xFFB551B2, 0xFFB75668, 0xFFB95B1E, 0xFFBB5FD4, 0xFFBD648A, + 0xFFBF6940, 0xFFC16DF6, 0xFFC372AC, 0xFFC57762, 0xFFC77C18, 0xFFC980CE, 0xFFCB8584, 0xFFCD8A3A, + 0xFFCF8EF0, 0xFFD193A6, 0xFFD3985C, 0xFFD59D12, 0xFFD7A1C8, 0xFFD9A67E, 0xFFDBAB34, 0xFFDDAFEA, + 0xFFDFB4A0, 0xFFE1B956, 0xFFE3BE0C, 0xFFE5C2C2, 0xFFE7C778, 0xFFE9CC2E, 0xFFEBD0E4, 0xFFEDD59A, + 0xFFEFDA50, 0xFFF1DF06, 0xFFF3E3BC, 0xFFF5E872, 0xFFF7ED28, 0xFFF9F1DE, 0xFFFBF694, 0xFFFDFB4A, + 0x00000000, 0x000204B6, 0x0004096C, 0x00060E22, 0x000812D8, 0x000A178E, 0x000C1C44, 0x000E20FA, + 0x001025B0, 0x00122A66, 0x00142F1C, 0x001633D2, 0x00183888, 0x001A3D3E, 0x001C41F4, 0x001E46AA, + 0x00204B60, 0x00225016, 0x002454CC, 0x00265982, 0x00285E38, 0x002A62EE, 0x002C67A4, 0x002E6C5A, + 0x00307110, 0x003275C6, 0x00347A7C, 0x00367F32, 0x003883E8, 0x003A889E, 0x003C8D54, 0x003E920A, + 0x004096C0, 0x00429B76, 0x0044A02C, 0x0046A4E2, 0x0048A998, 0x004AAE4E, 0x004CB304, 0x004EB7BA, + 0x0050BC70, 0x0052C126, 0x0054C5DC, 0x0056CA92, 0x0058CF48, 0x005AD3FE, 0x005CD8B4, 0x005EDD6A, + 0x0060E220, 0x0062E6D6, 0x0064EB8C, 0x0066F042, 0x0068F4F8, 0x006AF9AE, 0x006CFE64, 0x006F031A, + 0x007107D0, 0x00730C86, 0x0075113C, 0x007715F2, 0x00791AA8, 0x007B1F5E, 0x007D2414, 0x007F28CA, + 0x00812D80, 0x00833236, 0x008536EC, 0x00873BA2, 0x00894058, 0x008B450E, 0x008D49C4, 0x008F4E7A, + 0x00915330, 0x009357E6, 0x00955C9C, 0x00976152, 0x00996608, 0x009B6ABE, 0x009D6F74, 0x009F742A, + 0x00A178E0, 0x00A37D96, 0x00A5824C, 0x00A78702, 0x00A98BB8, 0x00AB906E, 0x00AD9524, 0x00AF99DA, + 0x00B19E90, 0x00B3A346, 0x00B5A7FC, 0x00B7ACB2, 0x00B9B168, 0x00BBB61E, 0x00BDBAD4, 0x00BFBF8A, + 0x00C1C440, 0x00C3C8F6, 0x00C5CDAC, 0x00C7D262, 0x00C9D718, 0x00CBDBCE, 0x00CDE084, 0x00CFE53A, + 0x00D1E9F0, 0x00D3EEA6, 0x00D5F35C, 0x00D7F812, 0x00D9FCC8, 0x00DC017E, 0x00DE0634, 0x00E00AEA, + 0x00E20FA0, 0x00E41456, 0x00E6190C, 0x00E81DC2, 0x00EA2278, 0x00EC272E, 0x00EE2BE4, 0x00F0309A, + 0x00F23550, 0x00F43A06, 0x00F63EBC, 0x00F84372, 0x00FA4828, 0x00FC4CDE, 0x00FE5194, 0x00100564A +}; +static const uint32_t yuv2rgbtable_gu[256] = { + 0xFFCDD300, 0xFFCE375A, 0xFFCE9BB4, 0xFFCF000E, 0xFFCF6468, 0xFFCFC8C2, 0xFFD02D1C, 0xFFD09176, + 0xFFD0F5D0, 0xFFD15A2A, 0xFFD1BE84, 0xFFD222DE, 0xFFD28738, 0xFFD2EB92, 0xFFD34FEC, 0xFFD3B446, + 0xFFD418A0, 0xFFD47CFA, 0xFFD4E154, 0xFFD545AE, 0xFFD5AA08, 0xFFD60E62, 0xFFD672BC, 0xFFD6D716, + 0xFFD73B70, 0xFFD79FCA, 0xFFD80424, 0xFFD8687E, 0xFFD8CCD8, 0xFFD93132, 0xFFD9958C, 0xFFD9F9E6, + 0xFFDA5E40, 0xFFDAC29A, 0xFFDB26F4, 0xFFDB8B4E, 0xFFDBEFA8, 0xFFDC5402, 0xFFDCB85C, 0xFFDD1CB6, + 0xFFDD8110, 0xFFDDE56A, 0xFFDE49C4, 0xFFDEAE1E, 0xFFDF1278, 0xFFDF76D2, 0xFFDFDB2C, 0xFFE03F86, + 0xFFE0A3E0, 0xFFE1083A, 0xFFE16C94, 0xFFE1D0EE, 0xFFE23548, 0xFFE299A2, 0xFFE2FDFC, 0xFFE36256, + 0xFFE3C6B0, 0xFFE42B0A, 0xFFE48F64, 0xFFE4F3BE, 0xFFE55818, 0xFFE5BC72, 0xFFE620CC, 0xFFE68526, + 0xFFE6E980, 0xFFE74DDA, 0xFFE7B234, 0xFFE8168E, 0xFFE87AE8, 0xFFE8DF42, 0xFFE9439C, 0xFFE9A7F6, + 0xFFEA0C50, 0xFFEA70AA, 0xFFEAD504, 0xFFEB395E, 0xFFEB9DB8, 0xFFEC0212, 0xFFEC666C, 0xFFECCAC6, + 0xFFED2F20, 0xFFED937A, 0xFFEDF7D4, 0xFFEE5C2E, 0xFFEEC088, 0xFFEF24E2, 0xFFEF893C, 0xFFEFED96, + 0xFFF051F0, 0xFFF0B64A, 0xFFF11AA4, 0xFFF17EFE, 0xFFF1E358, 0xFFF247B2, 0xFFF2AC0C, 0xFFF31066, + 0xFFF374C0, 0xFFF3D91A, 0xFFF43D74, 0xFFF4A1CE, 0xFFF50628, 0xFFF56A82, 0xFFF5CEDC, 0xFFF63336, + 0xFFF69790, 0xFFF6FBEA, 0xFFF76044, 0xFFF7C49E, 0xFFF828F8, 0xFFF88D52, 0xFFF8F1AC, 0xFFF95606, + 0xFFF9BA60, 0xFFFA1EBA, 0xFFFA8314, 0xFFFAE76E, 0xFFFB4BC8, 0xFFFBB022, 0xFFFC147C, 0xFFFC78D6, + 0xFFFCDD30, 0xFFFD418A, 0xFFFDA5E4, 0xFFFE0A3E, 0xFFFE6E98, 0xFFFED2F2, 0xFFFF374C, 0xFFFF9BA6, + 0x00000000, 0x0000645A, 0x0000C8B4, 0x00012D0E, 0x00019168, 0x0001F5C2, 0x00025A1C, 0x0002BE76, + 0x000322D0, 0x0003872A, 0x0003EB84, 0x00044FDE, 0x0004B438, 0x00051892, 0x00057CEC, 0x0005E146, + 0x000645A0, 0x0006A9FA, 0x00070E54, 0x000772AE, 0x0007D708, 0x00083B62, 0x00089FBC, 0x00090416, + 0x00096870, 0x0009CCCA, 0x000A3124, 0x000A957E, 0x000AF9D8, 0x000B5E32, 0x000BC28C, 0x000C26E6, + 0x000C8B40, 0x000CEF9A, 0x000D53F4, 0x000DB84E, 0x000E1CA8, 0x000E8102, 0x000EE55C, 0x000F49B6, + 0x000FAE10, 0x0010126A, 0x001076C4, 0x0010DB1E, 0x00113F78, 0x0011A3D2, 0x0012082C, 0x00126C86, + 0x0012D0E0, 0x0013353A, 0x00139994, 0x0013FDEE, 0x00146248, 0x0014C6A2, 0x00152AFC, 0x00158F56, + 0x0015F3B0, 0x0016580A, 0x0016BC64, 0x001720BE, 0x00178518, 0x0017E972, 0x00184DCC, 0x0018B226, + 0x00191680, 0x00197ADA, 0x0019DF34, 0x001A438E, 0x001AA7E8, 0x001B0C42, 0x001B709C, 0x001BD4F6, + 0x001C3950, 0x001C9DAA, 0x001D0204, 0x001D665E, 0x001DCAB8, 0x001E2F12, 0x001E936C, 0x001EF7C6, + 0x001F5C20, 0x001FC07A, 0x002024D4, 0x0020892E, 0x0020ED88, 0x002151E2, 0x0021B63C, 0x00221A96, + 0x00227EF0, 0x0022E34A, 0x002347A4, 0x0023ABFE, 0x00241058, 0x002474B2, 0x0024D90C, 0x00253D66, + 0x0025A1C0, 0x0026061A, 0x00266A74, 0x0026CECE, 0x00273328, 0x00279782, 0x0027FBDC, 0x00286036, + 0x0028C490, 0x002928EA, 0x00298D44, 0x0029F19E, 0x002A55F8, 0x002ABA52, 0x002B1EAC, 0x002B8306, + 0x002BE760, 0x002C4BBA, 0x002CB014, 0x002D146E, 0x002D78C8, 0x002DDD22, 0x002E417C, 0x002EA5D6, + 0x002F0A30, 0x002F6E8A, 0x002FD2E4, 0x0030373E, 0x00309B98, 0x0030FFF2, 0x0031644C, 0x0031C8A6 +}; +static const uint32_t yuv2rgbtable_gv[256] = { + 0xFF97E900, 0xFF98B92E, 0xFF99895C, 0xFF9A598A, 0xFF9B29B8, 0xFF9BF9E6, 0xFF9CCA14, 0xFF9D9A42, + 0xFF9E6A70, 0xFF9F3A9E, 0xFFA00ACC, 0xFFA0DAFA, 0xFFA1AB28, 0xFFA27B56, 0xFFA34B84, 0xFFA41BB2, + 0xFFA4EBE0, 0xFFA5BC0E, 0xFFA68C3C, 0xFFA75C6A, 0xFFA82C98, 0xFFA8FCC6, 0xFFA9CCF4, 0xFFAA9D22, + 0xFFAB6D50, 0xFFAC3D7E, 0xFFAD0DAC, 0xFFADDDDA, 0xFFAEAE08, 0xFFAF7E36, 0xFFB04E64, 0xFFB11E92, + 0xFFB1EEC0, 0xFFB2BEEE, 0xFFB38F1C, 0xFFB45F4A, 0xFFB52F78, 0xFFB5FFA6, 0xFFB6CFD4, 0xFFB7A002, + 0xFFB87030, 0xFFB9405E, 0xFFBA108C, 0xFFBAE0BA, 0xFFBBB0E8, 0xFFBC8116, 0xFFBD5144, 0xFFBE2172, + 0xFFBEF1A0, 0xFFBFC1CE, 0xFFC091FC, 0xFFC1622A, 0xFFC23258, 0xFFC30286, 0xFFC3D2B4, 0xFFC4A2E2, + 0xFFC57310, 0xFFC6433E, 0xFFC7136C, 0xFFC7E39A, 0xFFC8B3C8, 0xFFC983F6, 0xFFCA5424, 0xFFCB2452, + 0xFFCBF480, 0xFFCCC4AE, 0xFFCD94DC, 0xFFCE650A, 0xFFCF3538, 0xFFD00566, 0xFFD0D594, 0xFFD1A5C2, + 0xFFD275F0, 0xFFD3461E, 0xFFD4164C, 0xFFD4E67A, 0xFFD5B6A8, 0xFFD686D6, 0xFFD75704, 0xFFD82732, + 0xFFD8F760, 0xFFD9C78E, 0xFFDA97BC, 0xFFDB67EA, 0xFFDC3818, 0xFFDD0846, 0xFFDDD874, 0xFFDEA8A2, + 0xFFDF78D0, 0xFFE048FE, 0xFFE1192C, 0xFFE1E95A, 0xFFE2B988, 0xFFE389B6, 0xFFE459E4, 0xFFE52A12, + 0xFFE5FA40, 0xFFE6CA6E, 0xFFE79A9C, 0xFFE86ACA, 0xFFE93AF8, 0xFFEA0B26, 0xFFEADB54, 0xFFEBAB82, + 0xFFEC7BB0, 0xFFED4BDE, 0xFFEE1C0C, 0xFFEEEC3A, 0xFFEFBC68, 0xFFF08C96, 0xFFF15CC4, 0xFFF22CF2, + 0xFFF2FD20, 0xFFF3CD4E, 0xFFF49D7C, 0xFFF56DAA, 0xFFF63DD8, 0xFFF70E06, 0xFFF7DE34, 0xFFF8AE62, + 0xFFF97E90, 0xFFFA4EBE, 0xFFFB1EEC, 0xFFFBEF1A, 0xFFFCBF48, 0xFFFD8F76, 0xFFFE5FA4, 0xFFFF2FD2, + 0x00000000, 0x0000D02E, 0x0001A05C, 0x0002708A, 0x000340B8, 0x000410E6, 0x0004E114, 0x0005B142, + 0x00068170, 0x0007519E, 0x000821CC, 0x0008F1FA, 0x0009C228, 0x000A9256, 0x000B6284, 0x000C32B2, + 0x000D02E0, 0x000DD30E, 0x000EA33C, 0x000F736A, 0x00104398, 0x001113C6, 0x0011E3F4, 0x0012B422, + 0x00138450, 0x0014547E, 0x001524AC, 0x0015F4DA, 0x0016C508, 0x00179536, 0x00186564, 0x00193592, + 0x001A05C0, 0x001AD5EE, 0x001BA61C, 0x001C764A, 0x001D4678, 0x001E16A6, 0x001EE6D4, 0x001FB702, + 0x00208730, 0x0021575E, 0x0022278C, 0x0022F7BA, 0x0023C7E8, 0x00249816, 0x00256844, 0x00263872, + 0x002708A0, 0x0027D8CE, 0x0028A8FC, 0x0029792A, 0x002A4958, 0x002B1986, 0x002BE9B4, 0x002CB9E2, + 0x002D8A10, 0x002E5A3E, 0x002F2A6C, 0x002FFA9A, 0x0030CAC8, 0x00319AF6, 0x00326B24, 0x00333B52, + 0x00340B80, 0x0034DBAE, 0x0035ABDC, 0x00367C0A, 0x00374C38, 0x00381C66, 0x0038EC94, 0x0039BCC2, + 0x003A8CF0, 0x003B5D1E, 0x003C2D4C, 0x003CFD7A, 0x003DCDA8, 0x003E9DD6, 0x003F6E04, 0x00403E32, + 0x00410E60, 0x0041DE8E, 0x0042AEBC, 0x00437EEA, 0x00444F18, 0x00451F46, 0x0045EF74, 0x0046BFA2, + 0x00478FD0, 0x00485FFE, 0x0049302C, 0x004A005A, 0x004AD088, 0x004BA0B6, 0x004C70E4, 0x004D4112, + 0x004E1140, 0x004EE16E, 0x004FB19C, 0x005081CA, 0x005151F8, 0x00522226, 0x0052F254, 0x0053C282, + 0x005492B0, 0x005562DE, 0x0056330C, 0x0057033A, 0x0057D368, 0x0058A396, 0x005973C4, 0x005A43F2, + 0x005B1420, 0x005BE44E, 0x005CB47C, 0x005D84AA, 0x005E54D8, 0x005F2506, 0x005FF534, 0x0060C562, + 0x00619590, 0x006265BE, 0x006335EC, 0x0064061A, 0x0064D648, 0x0065A676, 0x006676A4, 0x006746D2 +}; +static const uint32_t yuv2rgbtable_bv[256] = { + 0xFF33A280, 0xFF353B3B, 0xFF36D3F6, 0xFF386CB1, 0xFF3A056C, 0xFF3B9E27, 0xFF3D36E2, 0xFF3ECF9D, + 0xFF406858, 0xFF420113, 0xFF4399CE, 0xFF453289, 0xFF46CB44, 0xFF4863FF, 0xFF49FCBA, 0xFF4B9575, + 0xFF4D2E30, 0xFF4EC6EB, 0xFF505FA6, 0xFF51F861, 0xFF53911C, 0xFF5529D7, 0xFF56C292, 0xFF585B4D, + 0xFF59F408, 0xFF5B8CC3, 0xFF5D257E, 0xFF5EBE39, 0xFF6056F4, 0xFF61EFAF, 0xFF63886A, 0xFF652125, + 0xFF66B9E0, 0xFF68529B, 0xFF69EB56, 0xFF6B8411, 0xFF6D1CCC, 0xFF6EB587, 0xFF704E42, 0xFF71E6FD, + 0xFF737FB8, 0xFF751873, 0xFF76B12E, 0xFF7849E9, 0xFF79E2A4, 0xFF7B7B5F, 0xFF7D141A, 0xFF7EACD5, + 0xFF804590, 0xFF81DE4B, 0xFF837706, 0xFF850FC1, 0xFF86A87C, 0xFF884137, 0xFF89D9F2, 0xFF8B72AD, + 0xFF8D0B68, 0xFF8EA423, 0xFF903CDE, 0xFF91D599, 0xFF936E54, 0xFF95070F, 0xFF969FCA, 0xFF983885, + 0xFF99D140, 0xFF9B69FB, 0xFF9D02B6, 0xFF9E9B71, 0xFFA0342C, 0xFFA1CCE7, 0xFFA365A2, 0xFFA4FE5D, + 0xFFA69718, 0xFFA82FD3, 0xFFA9C88E, 0xFFAB6149, 0xFFACFA04, 0xFFAE92BF, 0xFFB02B7A, 0xFFB1C435, + 0xFFB35CF0, 0xFFB4F5AB, 0xFFB68E66, 0xFFB82721, 0xFFB9BFDC, 0xFFBB5897, 0xFFBCF152, 0xFFBE8A0D, + 0xFFC022C8, 0xFFC1BB83, 0xFFC3543E, 0xFFC4ECF9, 0xFFC685B4, 0xFFC81E6F, 0xFFC9B72A, 0xFFCB4FE5, + 0xFFCCE8A0, 0xFFCE815B, 0xFFD01A16, 0xFFD1B2D1, 0xFFD34B8C, 0xFFD4E447, 0xFFD67D02, 0xFFD815BD, + 0xFFD9AE78, 0xFFDB4733, 0xFFDCDFEE, 0xFFDE78A9, 0xFFE01164, 0xFFE1AA1F, 0xFFE342DA, 0xFFE4DB95, + 0xFFE67450, 0xFFE80D0B, 0xFFE9A5C6, 0xFFEB3E81, 0xFFECD73C, 0xFFEE6FF7, 0xFFF008B2, 0xFFF1A16D, + 0xFFF33A28, 0xFFF4D2E3, 0xFFF66B9E, 0xFFF80459, 0xFFF99D14, 0xFFFB35CF, 0xFFFCCE8A, 0xFFFE6745, + 0x00000000, 0x000198BB, 0x00033176, 0x0004CA31, 0x000662EC, 0x0007FBA7, 0x00099462, 0x000B2D1D, + 0x000CC5D8, 0x000E5E93, 0x000FF74E, 0x00119009, 0x001328C4, 0x0014C17F, 0x00165A3A, 0x0017F2F5, + 0x00198BB0, 0x001B246B, 0x001CBD26, 0x001E55E1, 0x001FEE9C, 0x00218757, 0x00232012, 0x0024B8CD, + 0x00265188, 0x0027EA43, 0x002982FE, 0x002B1BB9, 0x002CB474, 0x002E4D2F, 0x002FE5EA, 0x00317EA5, + 0x00331760, 0x0034B01B, 0x003648D6, 0x0037E191, 0x00397A4C, 0x003B1307, 0x003CABC2, 0x003E447D, + 0x003FDD38, 0x004175F3, 0x00430EAE, 0x0044A769, 0x00464024, 0x0047D8DF, 0x0049719A, 0x004B0A55, + 0x004CA310, 0x004E3BCB, 0x004FD486, 0x00516D41, 0x005305FC, 0x00549EB7, 0x00563772, 0x0057D02D, + 0x005968E8, 0x005B01A3, 0x005C9A5E, 0x005E3319, 0x005FCBD4, 0x0061648F, 0x0062FD4A, 0x00649605, + 0x00662EC0, 0x0067C77B, 0x00696036, 0x006AF8F1, 0x006C91AC, 0x006E2A67, 0x006FC322, 0x00715BDD, + 0x0072F498, 0x00748D53, 0x0076260E, 0x0077BEC9, 0x00795784, 0x007AF03F, 0x007C88FA, 0x007E21B5, + 0x007FBA70, 0x0081532B, 0x0082EBE6, 0x008484A1, 0x00861D5C, 0x0087B617, 0x00894ED2, 0x008AE78D, + 0x008C8048, 0x008E1903, 0x008FB1BE, 0x00914A79, 0x0092E334, 0x00947BEF, 0x009614AA, 0x0097AD65, + 0x00994620, 0x009ADEDB, 0x009C7796, 0x009E1051, 0x009FA90C, 0x00A141C7, 0x00A2DA82, 0x00A4733D, + 0x00A60BF8, 0x00A7A4B3, 0x00A93D6E, 0x00AAD629, 0x00AC6EE4, 0x00AE079F, 0x00AFA05A, 0x00B13915, + 0x00B2D1D0, 0x00B46A8B, 0x00B60346, 0x00B79C01, 0x00B934BC, 0x00BACD77, 0x00BC6632, 0x00BDFEED, + 0x00BF97A8, 0x00C13063, 0x00C2C91E, 0x00C461D9, 0x00C5FA94, 0x00C7934F, 0x00C92C0A, 0x00CAC4C5 +}; + +#define OUT(x) \ + out[OUTITER] = (uint8_t)*(decode_surface + x)&0xFF; \ + OUTITER += OUTINC; + +#define OUT4(x) \ + OUT(x + 0x03); \ + OUT(x + 0x02); \ + OUT(x + 0x01); \ + OUT(x + 0x00); + +#define OUT8(x) \ + OUT4(x + 0x04); \ + OUT4(x + 0x00); + +#define OUT_LU_16A(x) \ + OUT8(x); \ + OUT8(x + 0x40); + +#define OUT_CH_8A(x) \ + OUT4(x); \ + OUT4(x + 0x20); + +//pppppppppppppppp +//x: macroblock address +//l: line 0-15 +#define OUT_LU_16(x,l) \ + OUT_LU_16A(x + (l/4) * 0x10 + (l%2) * 0x80 + ((l/2)%2?0x00:0x08)); + +//pppppppp +//x: macroblock address +//l: line 0-7 +//b: 0=cr 1=cb +#define OUT_CH_8(x,l,b) \ + OUT_CH_8A(x + (l/4) * 0x10 + (l%2) * 0x40 + ((l/2)%2?0x00:0x08) + (b?0x04:0x00)); + +//---- +#define CLAMP(x) ((x < 0) ? 0 : ((x > 255) ? 255 : x)) +#define SWAP(x,y) { x ^= y; y ^= x; x ^= y; } + +/* TODO: aspect ratio correction and PIP */ +bool cVideo::GetScreenImage(unsigned char * &video, int &xres, int &yres, bool get_video, bool get_osd, bool scale_to_video) +{ + lt_info("%s: get_video: %d get_osd: %d scale_to_video: %d\n", + __func__, get_video, get_osd, scale_to_video); + return true; +} diff --git a/libarmbox/video_lib.h b/libarmbox/video_lib.h new file mode 100644 index 0000000..e172cb9 --- /dev/null +++ b/libarmbox/video_lib.h @@ -0,0 +1,218 @@ +#ifndef _VIDEO_TD_H +#define _VIDEO_TD_H + +#include +#include "../common/cs_types.h" +#include "dmx_lib.h" + +typedef enum { + ANALOG_SD_RGB_CINCH = 0x00, + ANALOG_SD_YPRPB_CINCH, + ANALOG_HD_RGB_CINCH, + ANALOG_HD_YPRPB_CINCH, + ANALOG_SD_RGB_SCART = 0x10, + ANALOG_SD_YPRPB_SCART, + ANALOG_HD_RGB_SCART, + ANALOG_HD_YPRPB_SCART, + ANALOG_SCART_MASK = 0x10 +} analog_mode_t; + +typedef enum { + COLORFORMAT_RGB = 0x10, // keep compatible with analog_mode_t + COLORFORMAT_YUV, + COLORFORMAT_CVBS, + COLORFORMAT_SVIDEO, + COLORFORMAT_HDMI_RGB, + COLORFORMAT_HDMI_YCBCR444, + COLORFORMAT_HDMI_YCBCR422 +} COLOR_FORMAT; + +typedef enum { + VIDEO_FORMAT_MPEG2 = 0, + VIDEO_FORMAT_MPEG4_H264, + VIDEO_FORMAT_VC1, + VIDEO_FORMAT_JPEG, + VIDEO_FORMAT_GIF, + VIDEO_FORMAT_PNG, + VIDEO_FORMAT_MPEG4_H265, + VIDEO_FORMAT_AVS = 16 +} VIDEO_FORMAT; + +typedef enum { + VIDEO_SD = 0, + VIDEO_HD, + VIDEO_120x60i, + VIDEO_320x240i, + VIDEO_1440x800i, + VIDEO_360x288i +} VIDEO_DEFINITION; + +typedef enum { + VIDEO_FRAME_RATE_23_976 = 0, + VIDEO_FRAME_RATE_24, + VIDEO_FRAME_RATE_25, + VIDEO_FRAME_RATE_29_97, + VIDEO_FRAME_RATE_30, + VIDEO_FRAME_RATE_50, + VIDEO_FRAME_RATE_59_94, + VIDEO_FRAME_RATE_60 +} VIDEO_FRAME_RATE; + +typedef enum { + DISPLAY_AR_1_1, + DISPLAY_AR_4_3, + DISPLAY_AR_14_9, + DISPLAY_AR_16_9, + DISPLAY_AR_20_9, + DISPLAY_AR_RAW +} DISPLAY_AR; + +typedef enum { + DISPLAY_AR_MODE_PANSCAN = 0, + DISPLAY_AR_MODE_LETTERBOX, + DISPLAY_AR_MODE_NONE, + DISPLAY_AR_MODE_PANSCAN2 +} DISPLAY_AR_MODE; + +typedef enum { + VIDEO_DB_DR_NEITHER = 0, + VIDEO_DB_ON, + VIDEO_DB_DR_BOTH +} VIDEO_DB_DR; + +typedef enum { + VIDEO_PLAY_STILL = 0, + VIDEO_PLAY_CLIP, + VIDEO_PLAY_TRICK, + VIDEO_PLAY_MOTION, + VIDEO_PLAY_MOTION_NO_SYNC +} VIDEO_PLAY_MODE; + +typedef enum { + VIDEO_STD_NTSC, + VIDEO_STD_SECAM, + VIDEO_STD_PAL, + VIDEO_STD_480P, + VIDEO_STD_576P, + VIDEO_STD_720P60, + VIDEO_STD_1080I60, + VIDEO_STD_720P50, + VIDEO_STD_1080I50, + VIDEO_STD_1080P30, + VIDEO_STD_1080P24, + VIDEO_STD_1080P25, + VIDEO_STD_1080P50, + VIDEO_STD_1080P60, + VIDEO_STD_1080P2397, + VIDEO_STD_1080P2997, + VIDEO_STD_AUTO, + VIDEO_STD_MAX = VIDEO_STD_AUTO +} VIDEO_STD; + +/* not used, for dummy functions */ +typedef enum { + VIDEO_HDMI_CEC_MODE_OFF = 0, + VIDEO_HDMI_CEC_MODE_TUNER, + VIDEO_HDMI_CEC_MODE_RECORDER +} VIDEO_HDMI_CEC_MODE; + +typedef enum +{ + VIDEO_CONTROL_BRIGHTNESS = 0, + VIDEO_CONTROL_CONTRAST, + VIDEO_CONTROL_SATURATION, + VIDEO_CONTROL_HUE, + VIDEO_CONTROL_SHARPNESS, + VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS +} VIDEO_CONTROL; + + +class cVideo +{ + friend class cDemux; + friend class cPlayback; + private: + /* video device */ + int fd; + unsigned int devnum; + /* apparently we cannot query the driver's state + => remember it */ + video_play_state_t playstate; + int /*vidDispMode_t*/ croppingMode; + int /*vidOutFmt_t*/ outputformat; + int scartvoltage; + + VIDEO_FORMAT StreamType; + VIDEO_DEFINITION VideoDefinition; + DISPLAY_AR DisplayAR; + VIDEO_PLAY_MODE SyncMode; + DISPLAY_AR_MODE ARMode; + VIDEO_DB_DR eDbDr; + DISPLAY_AR PictureAR; + VIDEO_FRAME_RATE FrameRate; + int video_standby; + int64_t GetPTS(void); + + int brightness, contrast, saturation, hue; + + void openDevice(void); + void closeDevice(void); + public: + /* constructor & destructor */ + cVideo(int mode, void *, void *, unsigned int unit = 0); + ~cVideo(void); + + void * GetTVEnc() { return NULL; }; + void * GetTVEncSD() { return NULL; }; + + /* aspect ratio */ + int getAspectRatio(void); + void getPictureInfo(int &width, int &height, int &rate); + int setAspectRatio(int aspect, int mode); + + /* cropping mode */ + int setCroppingMode(int x = 0 /*vidDispMode_t x = VID_DISPMODE_NORM*/); + + /* get play state */ + int getPlayState(void); + + /* blank on freeze */ + int getBlank(void); + int setBlank(int enable); + + /* change video play state. Parameters are all unused. */ + int Start(void *PcrChannel = NULL, unsigned short PcrPid = 0, unsigned short VideoPid = 0, void *x = NULL); + int Stop(bool blank = true); + bool Pause(void); + + /* get video system infos */ + int GetVideoSystem(); + + /* set video_system */ + int SetVideoSystem(int video_system, bool remember = true); + int SetStreamType(VIDEO_FORMAT type); + void SetSyncMode(AVSYNC_TYPE mode); + bool SetCECMode(VIDEO_HDMI_CEC_MODE) { return true; }; + void SetCECAutoView(bool) { return; }; + void SetCECAutoStandby(bool) { return; }; + void ShowPicture(const char * fname, const char *_destname = NULL); + void StopPicture(); + void Standby(unsigned int bOn); + void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600, int startx = 0, int starty = 0, int endx = 1279, int endy = 719); + void SetControl(int, int); + void VideoParamWatchdog(void); + void setContrast(int val); + void SetVideoMode(analog_mode_t mode); + void SetDBDR(int) { return; }; + void SetAudioHandle(void *) { return; }; + void SetAutoModes(int [VIDEO_STD_MAX]) { return; }; + int OpenVBI(int) { return 0; }; + int CloseVBI(void) { return 0; }; + int StartVBI(unsigned short) { return 0; }; + int StopVBI(void) { return 0; }; + void SetDemux(cDemux *dmx); + void SetColorFormat(COLOR_FORMAT color_format); + bool GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video = true, bool get_osd = false, bool scale_to_video = false); +}; + +#endif