PIN input CIR support change
authorNIIBE Yutaka <gniibe@fsij.org>
Tue, 1 Nov 2011 05:57:11 +0000 (14:57 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 1 Nov 2011 05:57:11 +0000 (14:57 +0900)
ChangeLog
boards/STBEE/board.h
boards/STBEE_MINI/board.h
boards/STM8S_DISCOVERY/board.h
src/gnuk.h
src/main.c
src/openpgp.c
src/pin-cir.c
src/usb_prop.c

index 93136de..71de366 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,30 @@
 2011-11-01  Niibe Yutaka  <gniibe@fsij.org>
 
+       * boards/STBEE_MINI/board.h (TIMx): Define.
+       boards/STBEE/board.h (TIMx): Ditto.
+       boards/STM8S_DISCOVERY/board.h: Ditto.
+
+       * src/pin-cir.c (pinpad_getline): New.
+       (cir_timer_interrupt, cir_ext_interrupt): Use TIMx.
+       (cir_key_is_backspace, cir_key_is_enter, pin_main, pindisp):
+       Remove.
+       (cir_codetable_dell_mr425, cir_codetable_aquos)
+       (cir_codetable_regza, cir_codetable_bravia, ch_is_backspace)
+       (ch_is_enter, find_char_codetable, hex, cir_getchar): New.
+       (cir_timer_interrupt): Don't filter out ADDRESS.
+
+       * src/openpgp.c (get_pinpad_input): Don't invoke thread,
+       but just call pinpad_getline.
+
+       * src/main.c (display_interaction, display_fatal_code)
+       (display_status_code, led_blink): New.
+       (main): Call display_* routine.
+       (fatal): Notify main thread.
+       * src/usb_prop.c (gnuk_device_SetConfiguration): Notify main
+       thread.
+
+       * src/pin-cir.c (pindisp): Remove.
+
        * boards/FST_01_00: New (for 8MHz FST-01).
 
        * src/ac.c (calc_md): Fix comparison.
index 6e2e74d..a947f06 100644 (file)
 
 #if defined(PINPAD_SUPPORT)
 #define HAVE_7SEGLED   1
+/*
+ * Timer assignment for CIR
+ */
+#define TIMx   TIM3
 #endif
 
 /*
index 8238c91..a256510 100644 (file)
 
 #if defined(PINPAD_SUPPORT)
 #define HAVE_7SEGLED   1
+/*
+ * Timer assignment for CIR
+ */
+#define TIMx   TIM3
 #endif
 
 /*
index e0a2a5e..562dd55 100644 (file)
  */
 #define GPIOA_LED                8
 
+/*
+ * Timer assignment for CIR
+ */
+#define TIMx   TIM3
+
 /*
  * I/O ports initial setup, this configuration is established soon after reset
  * in the initialization code.
index 196e4ed..5e3d4f7 100644 (file)
@@ -348,5 +348,16 @@ extern void dial_sw_enable (void);
 extern uint8_t pin_input_buffer[MAX_PIN_CHARS];
 extern uint8_t pin_input_len;
 
-extern msg_t pin_main (void *arg);
+extern int pinpad_getline (int msg_code, systime_t timeout);
+
+#define LED_ONESHOT_SHORT ((eventmask_t)1)
+#define LED_ONESHOT_LONG  ((eventmask_t)2)
+#define LED_TWOSHOT       ((eventmask_t)4)
+#define LED_STATUS_MODE   ((eventmask_t)8)
+#define LED_INPUT_MODE   ((eventmask_t)16)
+#define LED_FATAL_MODE   ((eventmask_t)32)
+extern Thread *main_thread;
+extern void led_blink (int spec);
+
+
 #endif
index 9344ee2..655aa59 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * main.c - main routine of Gnuk
  *
- * Copyright (C) 2010 Free Software Initiative of Japan
+ * Copyright (C) 2010, 2011 Free Software Initiative of Japan
  * Author: NIIBE Yutaka <gniibe@fsij.org>
  *
  * This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -196,6 +196,161 @@ device_initialize_once (void)
 
 static volatile uint8_t fatal_code;
 
+Thread *main_thread;
+
+#define GNUK_INIT           0
+#define GNUK_RUNNING        1
+#define GNUK_INPUT_WAIT     2
+#define GNUK_FATAL        255
+/*
+ * 0 for initializing
+ * 1 for normal mode
+ * 2 for input waiting
+ * 255 for fatal
+ */
+static uint8_t main_mode;
+
+static void display_interaction (void)
+{
+  eventmask_t m;
+
+  while (1)
+    {
+      m = chEvtWaitOne (ALL_EVENTS);
+      set_led (1);
+      switch (m)
+       {
+       case LED_ONESHOT_SHORT:
+         chThdSleep (MS2ST (100));
+         break;
+       case LED_ONESHOT_LONG:
+         chThdSleep (MS2ST (400));
+         break;
+       case LED_TWOSHOT:
+         chThdSleep (MS2ST (50));
+         set_led (0);
+         chThdSleep (MS2ST (50));
+         set_led (1);
+         chThdSleep (MS2ST (50));
+         break;
+       case LED_STATUS_MODE:
+         chThdSleep (MS2ST (400));
+         set_led (0);
+         return;
+       case LED_FATAL_MODE:
+         main_mode = GNUK_FATAL;
+         set_led (0);
+         return;
+       default:
+         break;
+       }
+      set_led (0);
+    }
+}
+
+static void display_fatal_code (void)
+{
+  set_led (1);
+  chThdSleep (LED_TIMEOUT_ZERO);
+  set_led (0);
+  chThdSleep (LED_TIMEOUT_INTERVAL);
+  set_led (1);
+  chThdSleep (LED_TIMEOUT_ZERO);
+  set_led (0);
+  chThdSleep (LED_TIMEOUT_INTERVAL);
+  set_led (1);
+  chThdSleep (LED_TIMEOUT_ZERO);
+  set_led (0);
+  chThdSleep (LED_TIMEOUT_STOP);
+  set_led (1);
+  if (fatal_code & 1)
+    chThdSleep (LED_TIMEOUT_ONE);
+  else
+    chThdSleep (LED_TIMEOUT_ZERO);
+  set_led (0);
+  chThdSleep (LED_TIMEOUT_INTERVAL);
+  set_led (1);
+  if (fatal_code & 2)
+    chThdSleep (LED_TIMEOUT_ONE);
+  else
+    chThdSleep (LED_TIMEOUT_ZERO);
+  set_led (0);
+  chThdSleep (LED_TIMEOUT_INTERVAL);
+  set_led (1);
+  chThdSleep (LED_TIMEOUT_STOP);
+  set_led (0);
+  chThdSleep (LED_TIMEOUT_INTERVAL);
+}
+
+static void display_status_code (void)
+{
+  if (icc_state == ICC_STATE_START)
+    {
+      set_led (1);
+      chThdSleep (LED_TIMEOUT_ONE);
+      set_led (0);
+      chThdSleep (LED_TIMEOUT_STOP * 3);
+    }
+  else
+    /* GPGthread  running */
+    {
+      set_led (1);
+      if ((auth_status & AC_ADMIN_AUTHORIZED) != 0)
+       chThdSleep (LED_TIMEOUT_ONE);
+      else
+       chThdSleep (LED_TIMEOUT_ZERO);
+      set_led (0);
+      chThdSleep (LED_TIMEOUT_INTERVAL);
+      set_led (1);
+      if ((auth_status & AC_OTHER_AUTHORIZED) != 0)
+       chThdSleep (LED_TIMEOUT_ONE);
+      else
+       chThdSleep (LED_TIMEOUT_ZERO);
+      set_led (0);
+      chThdSleep (LED_TIMEOUT_INTERVAL);
+      set_led (1);
+      if ((auth_status & AC_PSO_CDS_AUTHORIZED) != 0)
+       chThdSleep (LED_TIMEOUT_ONE);
+      else
+       chThdSleep (LED_TIMEOUT_ZERO);
+
+      if (icc_state == ICC_STATE_WAIT)
+       {
+         set_led (0);
+         chThdSleep (LED_TIMEOUT_STOP * 2);
+       }
+      else if (icc_state == ICC_STATE_RECEIVE)
+       {
+         set_led (0);
+         chThdSleep (LED_TIMEOUT_INTERVAL);
+         set_led (1);
+         chThdSleep (LED_TIMEOUT_ONE);
+         set_led (0);
+         chThdSleep (LED_TIMEOUT_STOP);
+       }
+      else
+       {
+         set_led (0);
+         chThdSleep (LED_TIMEOUT_INTERVAL);
+         set_led (1);
+         chThdSleep (LED_TIMEOUT_STOP);
+         set_led (0);
+         chThdSleep (LED_TIMEOUT_INTERVAL);
+       }
+    }
+}
+
+void
+led_blink (int spec)
+{
+  if (spec == 0)
+    chEvtSignal (main_thread, LED_ONESHOT_SHORT);
+  else if (spec == 1)
+    chEvtSignal (main_thread, LED_ONESHOT_LONG);
+  else
+    chEvtSignal (main_thread, LED_TWOSHOT);
+}
+
 /*
  * Entry point.
  *
@@ -210,6 +365,8 @@ main (int argc, char **argv)
   (void)argc;
   (void)argv;
 
+  main_thread = chThdSelf ();
+
   flash_unlock ();
   device_initialize_once ();
   usb_lld_init ();
@@ -231,105 +388,47 @@ main (int argc, char **argv)
 
   while (1)
     {
-      count++;
+      eventmask_t m;
 
-      if (fatal_code != 0)
+      count++;
+      m = chEvtWaitOneTimeout (ALL_EVENTS, LED_TIMEOUT_INTERVAL);
+      switch (m)
        {
+       case LED_STATUS_MODE:
+         main_mode = GNUK_RUNNING;
+         break;
+       case LED_FATAL_MODE:
+         main_mode = GNUK_FATAL;
+         break;
+       case LED_INPUT_MODE:
+         main_mode = GNUK_INPUT_WAIT;
          set_led (1);
-         chThdSleep (LED_TIMEOUT_ZERO);
-         set_led (0);
-         chThdSleep (LED_TIMEOUT_INTERVAL);
-         set_led (1);
-         chThdSleep (LED_TIMEOUT_ZERO);
+         chThdSleep (MS2ST (400));
          set_led (0);
-         chThdSleep (LED_TIMEOUT_INTERVAL);
-         set_led (1);
-         chThdSleep (LED_TIMEOUT_ZERO);
-         set_led (0);
-         chThdSleep (LED_TIMEOUT_STOP);
-         set_led (1);
-         if (fatal_code & 1)
-           chThdSleep (LED_TIMEOUT_ONE);
-         else
-           chThdSleep (LED_TIMEOUT_ZERO);
-         set_led (0);
-         chThdSleep (LED_TIMEOUT_INTERVAL);
-         set_led (1);
-         if (fatal_code & 2)
-           chThdSleep (LED_TIMEOUT_ONE);
-         else
-           chThdSleep (LED_TIMEOUT_ZERO);
-         set_led (0);
-         chThdSleep (LED_TIMEOUT_INTERVAL);
-         set_led (1);
-         chThdSleep (LED_TIMEOUT_STOP);
-         set_led (0);
-         chThdSleep (LED_TIMEOUT_INTERVAL);
-       } 
+         break;
+       default:
+         break;
+       }
 
-      if (bDeviceState != CONFIGURED)
+      switch (main_mode)
        {
+       case GNUK_FATAL:
+         display_fatal_code ();
+         break;
+       case GNUK_INIT:
          set_led (1);
          chThdSleep (LED_TIMEOUT_ZERO);
          set_led (0);
          chThdSleep (LED_TIMEOUT_STOP * 3);
-       }
-      else
-       /* Device configured */
-       if (icc_state == ICC_STATE_START)
-         {
-           set_led (1);
-           chThdSleep (LED_TIMEOUT_ONE);
-           set_led (0);
-           chThdSleep (LED_TIMEOUT_STOP * 3);
-         }
-       else
-         /* GPGthread  running */
-         {
-           set_led (1);
-           if ((auth_status & AC_ADMIN_AUTHORIZED) != 0)
-             chThdSleep (LED_TIMEOUT_ONE);
-           else
-             chThdSleep (LED_TIMEOUT_ZERO);
-           set_led (0);
-           chThdSleep (LED_TIMEOUT_INTERVAL);
-           set_led (1);
-           if ((auth_status & AC_OTHER_AUTHORIZED) != 0)
-             chThdSleep (LED_TIMEOUT_ONE);
-           else
-             chThdSleep (LED_TIMEOUT_ZERO);
-           set_led (0);
-           chThdSleep (LED_TIMEOUT_INTERVAL);
-           set_led (1);
-           if ((auth_status & AC_PSO_CDS_AUTHORIZED) != 0)
-             chThdSleep (LED_TIMEOUT_ONE);
-           else
-             chThdSleep (LED_TIMEOUT_ZERO);
-
-           if (icc_state == ICC_STATE_WAIT)
-             {
-               set_led (0);
-               chThdSleep (LED_TIMEOUT_STOP * 2);
-             }
-           else if (icc_state == ICC_STATE_RECEIVE)
-             {
-               set_led (0);
-               chThdSleep (LED_TIMEOUT_INTERVAL);
-               set_led (1);
-               chThdSleep (LED_TIMEOUT_ONE);
-               set_led (0);
-               chThdSleep (LED_TIMEOUT_STOP);
-             }
-           else
-             {
-               set_led (0);
-               chThdSleep (LED_TIMEOUT_INTERVAL);
-               set_led (1);
-               chThdSleep (LED_TIMEOUT_STOP);
-               set_led (0);
-               chThdSleep (LED_TIMEOUT_INTERVAL);
-             }
-         }
+         break;
+       case GNUK_INPUT_WAIT:
+         display_interaction ();
+         break;
+       case GNUK_RUNNING:
+       default:
+         display_status_code ();
+         break;
+       }         
 
 #ifdef DEBUG_MORE
       if (bDeviceState == CONFIGURED && (count % 10) == 0)
@@ -349,6 +448,7 @@ void
 fatal (uint8_t code)
 {
   fatal_code = code;
+  chEvtSignal (main_thread, LED_FATAL_MODE);
   _write ("fatal\r\n", 7);
   for (;;);
 }
index 172bf22..1738363 100644 (file)
@@ -97,24 +97,19 @@ gpg_fini (void)
 
 #if defined(PINPAD_SUPPORT)
 /* 
- * Invoke the thread PIN_MAIN, and let user input PIN string.
+ * Let user input PIN string.
  * Return length of the string.
  * The string itself is in PIN_INPUT_BUFFER.
  */
 static int
 get_pinpad_input (int msg_code)
 {
-  Thread *t;
+  int r;
 
-  t = chThdCreateFromHeap (NULL, THD_WA_SIZE (128),
-                          NORMALPRIO, pin_main, (void *)msg_code);
-  if (t == NULL)
-    return -1;
-  else
-    {
-      chThdWait (t);
-      return pin_input_len;
-    }
+  chEvtSignal (main_thread, LED_INPUT_MODE);
+  r = pinpad_getline (msg_code, MS2ST (8000));
+  chEvtSignal (main_thread, LED_STATUS_MODE);
+  return r;
 }
 #endif
 
index f512b94..75e84ef 100644 (file)
 #include "board.h"
 #include "gnuk.h"
 
-#if 0
+#ifdef DEBUG
 #define DEBUG_CIR 1
 #endif
 
 uint8_t pin_input_buffer[MAX_PIN_CHARS];
 uint8_t pin_input_len;
 
+/*
+ * Supported/tested TV controllers:
+ *
+ *   Controller of Toshiba REGZA
+ *   Controller of Sony BRAVIA
+ *   Controller of Sharp AQUOS
+ *   Dell Wireless Travel Remote MR425
+ *
+ * The code supports RC-5 protocol in fact, but I don't have any
+ * controller at hand which I can test with, so I don't have any data
+ * for controller of RC-5.
+ *
+ * Current code assumes following mapping:
+ *
+ *  --------------------------------------
+ *    Protocol          Controller
+ *  --------------------------------------
+ *     RC-6               Dell MR425
+ *     NEC                Toshiba REGZA
+ *     Sharp              Sharp AQUOS
+ *     Sony               Sony BRAVIA
+ *  --------------------------------------
+ *
+ * In future, when I will have other controllers, this mapping will be
+ * (should be) configurable, at compile time at least, preferably at
+ * runtime.
+ */
+
 /*
  * Philips RC-5 Protocol: 14-bit (MSB first)
  *
@@ -69,18 +97,26 @@ uint8_t pin_input_len;
  */
 
 /*
- * PB0 / TIM3_CH3
+ * The implementation note of CIR signal decoding (on STM32).
  *
- * 72MHz
+ * (1) Use EXTI interrupt to detect the first edge of signal.
+ * (2) Use Timer (with PWM input mode) to measure timings of square wave.
+ *
+ */
+
+/*
+ * Timer settings.
+ *
+ * See THE reference manual (RM0008) section 15.3.6 PWM input mode.
  *
+ * 72MHz
  * Prescaler = 72
  *
  * 1us
  *
- *
- * TIM3_CR1
- *  CKD  = 10 (sampling x4)
- *  ARPE = 0  (not buffered)
+ * TIMx_CR1
+ *  CKD  = 00 (tDTS = tCK_INT)
+ *  ARPE = 1  (buffered)
  *  CMS  = 00 (up counter)
  *  DIR  = 0  (up counter)
  *  OPM  = 0  (up counter)
@@ -88,13 +124,13 @@ uint8_t pin_input_len;
  *  UDIS = 0  (UEV (update event) enabled)
  *  CEN  = 1  (counter enable)
  *
- * TIM3_CR2
+ * TIMx_CR2
  *  TI1S = 1 (TI1 is XOR of ch1, ch2, ch3)
  *  MMS  = 000 (TRGO at Reset)
  *  CCDS = 0 (DMA on capture)
  *  RSVD = 000
  *
- * TIM3_SMCR
+ * TIMx_SMCR
  *  ETP  = 0
  *  ECE  = 0
  *  ETPS = 00
@@ -104,60 +140,37 @@ uint8_t pin_input_len;
  *  RSVD = 0
  *  SMS  = 100 (Reset-mode)
  *
- * TIM3_DIER
+ * TIMx_DIER
  *
- * TIM3_SR
+ * TIMx_SR
  *
- * TIM3_EGR
+ * TIMx_EGR
  *
- * TIM3_CCMR1
+ * TIMx_CCMR1
  *  CC1S = 01 (TI1 selected)
  *  CC2S = 10 (TI1 selected)
+ *  IC1F = 1001 (fSAMPLING=fDTS/8, N=8)
+ *  IC2F = 1001 (fSAMPLING=fDTS/8, N=8)
  *
- * TIM3_CCMR2
+ * TIMx_CCMR2
  *
- * TIM3_CCER
+ * TIMx_CCER
  *  CC2P = 1 (polarity = falling edge: TI1FP1)
  *  CC2E = 1
  *  CC1P = 0 (polarity = rising edge: TI1FP1)
  *  CC1E = 1
  *
- * TIM3_CNT
- * TIM3_PSC = 71
- * TIM3_ARR = 18000
+ * TIMx_CNT
+ * TIMx_PSC = 71
+ * TIMx_ARR = 18000
  *
- * TIM3_CCR1  period
- * TIM3_CCR2  duty
+ * TIMx_CCR1  period
+ * TIMx_CCR2  duty
  *
- * TIM3_DCR
- * TIM3_DMAR
+ * TIMx_DCR
+ * TIMx_DMAR
  */
 
-#define PINDISP_TIMEOUT_INTERVAL0      MS2ST(25)
-#define PINDISP_TIMEOUT_INTERVAL1      MS2ST(300)
-
-static void
-pindisp (uint8_t c)
-{
-#if defined(HAVE_7SEGLED)
-  switch (c)
-    {
-    case 'G':
-      palWritePort (IOPORT2, 0xa1ff);
-      break;
-    case 'P':
-      palWritePort (IOPORT2, 0x98ff);
-      break;
-    case '.':
-      palWritePort (IOPORT2, 0x7fff);
-      break;
-    default:
-      palWritePort (IOPORT2, 0xffff);
-    }
-#else
-  (void)c;
-#endif
-}
 
 #if defined(DEBUG_CIR)
 static uint16_t intr_ext;
@@ -179,14 +192,6 @@ static uint8_t cir_proto;
 #define CIR_PROTO_NEC   5
 #define CIR_PROTO_SHARP 6
 
-#define CIR_KEY_RC6_ENTER       0x0d   /* Mute */
-#define CIR_KEY_RC6_BACKSPACE   0xa4   /* <=   */
-#define CIR_KEY_NEC_ENTER       0x3d   /* 'kettei' */
-#define CIR_KEY_NEC_BACKSPACE   0x3b   /* 'modoru' */
-#define CIR_KEY_SONY_ENTER      0x65   /* 'kettei' */
-#define CIR_KEY_SONY_BACKSPACE  0xa3   /* 'modoru' */
-#define CIR_KEY_SHARP_ENTER     0x0252 /* 'kettei' */
-#define CIR_KEY_SHARP_BACKSPACE 0xe4   /* 'modoru' */
 
 /* CIR_DATA_ZERO: Used for zero-bit handling of RC-5/RC-6 */
 static uint8_t cir_data_zero;
@@ -196,17 +201,6 @@ static uint8_t cir_seq;
 static systime_t cir_input_last;
 #define CIR_PERIOD_INHIBIT_CHATTER 200 /* mili second */
 
-/*
- * RC-5 protocol doesn't have a start bit, while any other protocols
- * have the one.
- */
-#define CIR_BIT_START_RC5_DETECT 1600 /* RC-5: 889us, Sony start: 2400us */
-
-#define CIR_BIT_START_RC5_LENGTH (889 + 889/2)
-#define CIR_BIT_PERIOD_RC6 444
-#define CIR_BIT_PERIOD 1500
-#define CIR_BIT_SIRC_PERIOD_ON 1000
-
 static void
 cir_init (void)
 {
@@ -216,116 +210,440 @@ cir_init (void)
   cir_ext_enable ();
 }
 
-static Thread *pin_thread;
+
+#define CH_RETURN    0x0d
+#define CH_BACKSPACE 0x08
+
+struct codetable {
+  uint16_t cir_code;
+  uint8_t  char_code;
+};
+
+/* NOTE: no way to input '0' */
+static const struct codetable
+cir_codetable_dell_mr425[] = {
+  {0x10, '7' },          /* Speaker Louder       */
+  {0x11, '8' },          /* Speaker Quieter      */
+  {0x0d, '9' },          /* Speaker Mute         */
+  {0xce, 'a' },          /* Black triangle UP    */
+  {0xcf, 'b' },          /* Black triangle DOWN  */
+  {0x58, 'c' },          /* White triangle UP    */
+  {0x5a, 'd' },          /* White triangle LEFT  */
+  {0x5c, CH_RETURN },    /* Check                */
+  {0x5b, 'e' },          /* White triangle RIGHT */
+  {0xa4, CH_BACKSPACE }, /* Back                 */
+  {0x59, 'f' },          /* White triangle DOWN  */
+  {0x2f, '1' },          /* Rewind               */
+  {0x2c, '2' },          /* Play / Pause         */
+  {0x2e, '3' },          /* Forward              */
+  {0x21, '4' },          /* Skip backward        */
+  {0x31, '5' },          /* Stop                 */
+  {0x20, '6' },          /* Skip forward         */
+
+  {0, 0} /* <<END>>   */
+};
+
+#define CIR_ADDR_SHARP_AQUOS 0x028f
+static const struct codetable
+cir_codetable_aquos[] = {
+  { 0x0116, ' ' }, /* Power */
+  { 0x025e, '0' }, /* d */
+  { 0x024e, '1' }, /* 1 */
+  { 0x024f, '2' }, /* 2 */
+  { 0x0250, '3' }, /* 3 */
+  { 0x0251, '4' }, /* 4 */
+  { 0x0252, '5' }, /* 5 */
+  { 0x0253, '6' }, /* 6 */
+  { 0x0254, '7' }, /* 7 */
+  { 0x0255, '8' }, /* 8 */
+  { 0x0256, '9' }, /* 9 */
+  { 0x0257, 'a' }, /* 10/0 */
+  { 0x0258, 'b' }, /* 11 */
+  { 0x0259, 'c' }, /* 12 */
+  { 0x0111, 'd' }, /* Ch ^ */
+  { 0x0112, 'e' }, /* Ch v */
+  { 0x0114, 'f' }, /* Vol + */
+  { 0x0115, 'g' }, /* Vol - */
+  { 0x0117, 'h' }, /* Mute */
+  { 0x0280, 'i' }, /* BLUE */
+  { 0x0281, 'j' }, /* RED */
+  { 0x0282, 'k' }, /* GREEN */
+  { 0x0283, 'l' }, /* YELLOW */
+  { 0x011b, 'm' }, /* DISPLAY CONTROL (gamen hyouji)     */
+  { 0x01d5, 'n' }, /* DISPLAY SIZE */
+  { 0x0157, 'o' }, /* UP */
+  { 0x01d7, 'p' }, /* LEFT */
+  { 0x01d8, 'q' }, /* RIGHT */
+  { 0x0120, 'r' }, /* DOWN */
+  { 0x0152, CH_RETURN }, /* Commit (kettei) */
+  { 0x01e4, CH_BACKSPACE }, /* Back (modoru) */
+  { 0x01f5, 's' }, /* Quit (shuuryou) */
+  { 0x0b03, 't' }, /* Rewind (hayamodoshi) */
+  { 0x0b01, 'u' }, /* Play (saisei) */
+  { 0x0b04, 'v' }, /* Forward (hayaokuri) */
+  { 0x0b02, 'w' }, /* Stop (teishi) */
+  { 0x028a, 'x' }, /* BS */
+  { 0x028b, 'y' }, /* CS */
+  { 0x025f, 'z' }, /* Program information (bangumi jouhou) */
+  { 0x0260, '\\' }, /* Program table (bangumi hyou) */
+  { 0x0118, '|' }, /* Sound channel (onsei kirikae) */
+  { 0x028e, '[' }, /* Ground Analog (chijou A) */
+  { 0x0289, ']' }, /* Ground Digital (chijou D) */
+
+  { 0x0b07, '\"' }, /* Feature select (kinou sentaku) */
+  { 0x026b, '.' }, /* TV/Radio/Data (terebi/rajio/data) */
+  { 0x025a, ',' }, /* 3 code input (3 keta nyuuryoku) */
+  { 0x0267, ':' }, /* subtitle (jimaku) */
+  { 0x0159, ';' }, /* hold (seishi) */
+
+  { 0x01c4, 'A' }, /* Menu */
+  { 0x011a, 'B' }, /* Off timer */
+  { 0x0121, 'C' }, /* CATV */
+  { 0x0b05, 'D' }, /* Record */
+  { 0x0b06, 'E' }, /* Recording stop */
+  { 0x0113, 'F' }, /* Inputs (nyuuryoku kirikae) */
+  { 0x0275, 'G' }, /* other programs (ura bangumi) */
+  { 0x0266, 'H' }, /* signal control (eizou kirikae) */
+  { 0x01e7, 'I' }, /* AV position  */
+  { 0x027f, 'J' }, /* i.LINK */
+  { 0x0b00, 'K' }, /* Recorder power */
+  { 0x028f, 'L' }, /* as you like it (okonomi senkyoku) */
+
+  {0, 0} /* <<END>>   */
+};
+
+#define CIR_ADDR_TOSHIBA_REGZA 0xbf40
+static const struct codetable
+cir_codetable_regza[] = {
+  { 0x12, ' ' }, /* Power */
+  { 0x14, '0' }, /* d (data) */
+  { 0x01, '1' }, /* 1 */
+  { 0x02, '2' }, /* 2 */
+  { 0x03, '3' }, /* 3 */
+  { 0x04, '4' }, /* 4 */
+  { 0x05, '5' }, /* 5 */
+  { 0x06, '6' }, /* 6 */
+  { 0x07, '7' }, /* 7 */
+  { 0x08, '8' }, /* 8 */
+  { 0x09, '9' }, /* 9 */
+  { 0x0a, 'a' }, /* 10 */
+  { 0x0b, 'b' }, /* 11 */
+  { 0x0c, 'c' }, /* 12 */
+  { 0x1b, 'd' }, /* Ch ^ */
+  { 0x1f, 'e' }, /* Ch v */
+  { 0x1a, 'f' }, /* Vol + */
+  { 0x1e, 'g' }, /* Vol - */
+  { 0x10, 'h' }, /* Mute */
+  { 0x73, 'i' }, /* BLUE */
+  { 0x74, 'j' }, /* RED */
+  { 0x75, 'k' }, /* GREEN */
+  { 0x76, 'l' }, /* YELLOW */
+  { 0x1c, 'm' }, /* Display control */
+  { 0x2b, 'n' }, /* Display size */
+  { 0x3e, 'o' }, /* UP */
+  { 0x5f, 'p' }, /* LEFT */
+  { 0x5b, 'q' }, /* RIGHT */
+  { 0x3f, 'r' }, /* DOWN */
+  { 0x3d, CH_RETURN }, /* Commit (kettei) */
+  { 0x3b, CH_BACKSPACE }, /* Back (modoru) */
+  { 0x3c, 's' }, /* Quit (shuuryou) */
+  { 0x2c, 't' }, /* << (Rewind) */
+  { 0x2d, 'u' }, /* >/|| (Play/Stop) */
+  { 0x2e, 'v' }, /* >> (Forward) */
+  { 0x2b, 'w' }, /* Stop (teishi) */
+  { 0x7c, 'x' }, /* BS */
+  { 0x7d, 'y' }, /* CS */
+  { 0x71, 'z' }, /* Program information (bangumi setsumei) */
+  { 0x77, '\\' }, /* Mini program table (mini bangumihyou) */
+  { 0x13, '|' }, /* Sound (onta kirikae) */
+  { 0x7a, '[' }, /* Ground Digital (chideji) */
+  { 0x7b, ']' }, /* Ground Analog (chiana) */
+
+  { 0xd0, '\"' }, /* Settings Menu (settei menu) */
+  { 0x6d, '.' }, /* Radio/Data (rajio/data) */
+  { 0x60, ',' }, /* CH 10-key input (search) */
+  { 0x52, ':' }, /* subtitle (jimaku) */
+  { 0x50, ';' }, /* hold (seishi) */
+
+  { 0x3a, 'A' }, /* Input- (nyuuryokukirikae-) */
+  { 0x0f, 'B' }, /* Input+ (nyuuryokukirikae+) */
+  { 0x29, 'C' }, /* Two screens (nigamen) */
+  { 0x25, 'D' }, /* Broadband */
+  { 0x27, 'E' }, /* |<< Skip backward */
+  { 0x26, 'F' }, /* >>| Skip forward  */
+  { 0x61, '!' }, /* 1 NHK1 */
+  { 0x62, '@' }, /* 2 NHK2 */
+  { 0x63, '#' }, /* 3 NHKh */
+  { 0x64, '$' }, /* 4 BS Nihon TV */
+  { 0x65, '%' }, /* 5 BS Asashi */
+  { 0x66, '^' }, /* 6 BS-i */
+  { 0x67, '&' }, /* 7 BSJ */
+  { 0x68, '*' }, /* 8 BS Fuji */
+  { 0x69, '(' }, /* 9 WOW */
+  { 0x6a, ')' }, /* 10 Star */
+  { 0x6b, '-' }, /* 11 BS11 */
+  { 0x6c, '+' }, /* 12 TwellV */
+  { 0x27, '=' }, /* Quick (Delete) */
+  { 0x34, '<' }, /* REGZA link */
+  { 0x6e, '>' }, /* Program Table */
+  { 0x20, '/' }, /* ^^ */
+  { 0x22, '\'' }, /* << */
+  { 0x23, '?' }, /* >> */
+  { 0x21, '_' }, /* vv */
+
+  {0, 0} /* <<END>>   */
+};
+
+static const struct codetable
+cir_codetable_bravia[] = {
+  { 0x15, ' ' }, /* Power */
+  { 0x95, '0' }, /* d (16-bit: 0x4b) */
+  { 0x00, '1' }, /* 1 */
+  { 0x01, '2' }, /* 2 */
+  { 0x02, '3' }, /* 3 */
+  { 0x03, '4' }, /* 4 */
+  { 0x04, '5' }, /* 5 */
+  { 0x05, '6' }, /* 6 */
+  { 0x06, '7' }, /* 7 */
+  { 0x07, '8' }, /* 8 */
+  { 0x08, '9' }, /* 9 */
+  { 0x09, 'a' }, /* 10 */
+  { 0x0a, 'b' }, /* 11 */
+  { 0x0b, 'c' }, /* 12 */
+  { 0x10, 'd' }, /* CH+ */
+  { 0x11, 'd' }, /* CH- */
+  { 0x12, 'f' }, /* Vol+ */
+  { 0x13, 'g' }, /* Vol- */
+  { 0x14, 'h' }, /* Mute */
+  { 0xa4, 'i' }, /* BLUE (16-bit: 0x4b) */
+  { 0xa5, 'j' }, /* RED (16-bit: 0x4b) */
+  { 0xa6, 'k' }, /* GREEN (16-bit: 0x4b) */
+  { 0xa7, 'l' }, /* YELLOW (16-bit: 0x4b) */
+  { 0x3a, 'm' }, /* DISPLAY control (gamen hyouji) */
+  { 0x3d, 'n' }, /* Display Wide (waido kirikae) */
+  { 0x74, 'o' }, /* UP */
+  { 0x75, 'p' }, /* DOWN */
+  { 0x33, 'q' }, /* RIGHT */
+  { 0x34, 'r' }, /* LEFT */
+  { 0x65, CH_RETURN }, /* Commit (kettei) */
+  { 0xa3, CH_BACKSPACE }, /* Back (modoru) (16-bit: 0x4b) */
+  { 0xac, 's' }, /* BS (16-bit: 0x4b) */
+  { 0xab, 't' }, /* CS (16-bit: 0x4b) */
+  { 0x5b, 'u' }, /* Program table (bangumi hyou) (16-bit: 0x52) */
+  { 0x17, 'v' }, /* Sound channel (onsei kirikae) */
+  { 0xa8, 'w' }, /* subtitle (jimaku) (16-bit: 0x4b) */
+  { 0x5c, 'x' }, /* hold (memo) */
+  { 0xb6, 'y' }, /* Tool (16-bit: 0x4b) */
+  { 0x8c, 'z' }, /* 10 key input (10ki-) (16-bit: 0x4b) */
+  { 0x60, '!' }, /* Menu */
+  { 0xae, '@' }, /* Analog (16-bit: 0x4b) */
+  { 0xb2, '#' }, /* Digital (16-bit: 0x4b) */
+  { 0x25, '$' }, /* Input (nyuuryoku kirikae) */
+
+  {0, 0} /* <<END>>   */,
+};
 
 static int
-cir_key_is_backspace (void)
+ch_is_backspace (int ch)
 {
-  return (cir_proto == CIR_PROTO_RC6 && cir_data == CIR_KEY_RC6_BACKSPACE)
-    || (cir_proto == CIR_PROTO_NEC && cir_data == CIR_KEY_NEC_BACKSPACE)
-    || (cir_proto == CIR_PROTO_SONY && cir_data == CIR_KEY_SONY_BACKSPACE)
-    || (cir_proto == CIR_PROTO_SHARP && cir_data == CIR_KEY_SHARP_BACKSPACE);
+  return ch == CH_BACKSPACE;
 }
 
 static int
-cir_key_is_enter (void)
+ch_is_enter (int ch)
 {
-  return (cir_proto == CIR_PROTO_RC6 && cir_data == CIR_KEY_RC6_ENTER)
-    || (cir_proto == CIR_PROTO_NEC && cir_data == CIR_KEY_NEC_ENTER)
-    || (cir_proto == CIR_PROTO_SONY && cir_data == CIR_KEY_SONY_ENTER)
-    || (cir_proto == CIR_PROTO_SHARP && cir_data == CIR_KEY_SHARP_ENTER);
+  return ch == CH_RETURN;
 }
 
-msg_t
-pin_main (void *arg)
+/* liner search is good enough for this small amount of data */
+static uint8_t
+find_char_codetable (uint32_t cir_code, const struct codetable *ctp)
 {
-  uint8_t s = 0;
-  int msg_code = (int)arg;
+  while (ctp->cir_code != 0x0000 || ctp->char_code != 0x00)
+    if (ctp->cir_code == cir_code)
+      return ctp->char_code;
+    else
+      ctp++;
+
+  /* Not found */
+  return cir_code & 0xff;
+}
 
-  (void)msg_code;
-  pin_thread = chThdSelf ();
+static int
+hex (int x)
+{
+  if (x < 10)
+    return x + '0';
+  else
+    return (x - 10) + 'a';
+}
+
+static int
+cir_getchar (systime_t timeout)
+{
+  uint16_t cir_addr;
+  eventmask_t m;
+#if defined(DEBUG_CIR)
+  uint16_t *p;
+#endif
 
 #if defined(DEBUG_CIR)
   cirinput_p = cirinput;
 #endif
 
-  pin_input_len = 0;
   chEvtClear (ALL_EVENTS);
   cir_init ();
 
-  while (!chThdShouldTerminate ())
-    {
-      eventmask_t m;
-
-      m = chEvtWaitOneTimeout (ALL_EVENTS, PINDISP_TIMEOUT_INTERVAL1);
+  m = chEvtWaitOneTimeout (ALL_EVENTS, timeout);
+  if (m == 0)
+    return -1;
 
-      if (m)
-       {
 #if defined(DEBUG_CIR)
-         uint16_t *p;
-
-         DEBUG_INFO ("****\r\n");
-         DEBUG_SHORT (intr_ext);
-         DEBUG_SHORT (intr_trg);
-         DEBUG_SHORT (intr_ovf);
-         DEBUG_INFO ("----\r\n");
-         for (p = cirinput; p < cirinput_p; p++)
-           DEBUG_SHORT (*p);
-         DEBUG_INFO ("====\r\n");
-
-         cirinput_p = cirinput;
-#endif
-         DEBUG_INFO ("**** CIR data:");
-         DEBUG_WORD (cir_data);
-         if (cir_seq > 48)
-           {
-             DEBUG_SHORT (cir_data_more);
-           }
-         DEBUG_BYTE (cir_seq);
+  DEBUG_INFO ("****\r\n");
+  DEBUG_SHORT (intr_ext);
+  DEBUG_SHORT (intr_trg);
+  DEBUG_SHORT (intr_ovf);
+  DEBUG_INFO ("----\r\n");
+  for (p = cirinput; p < cirinput_p; p++)
+    DEBUG_SHORT (*p);
+  DEBUG_INFO ("====\r\n");
 
-         if (cir_key_is_backspace ())
-           {
-             if (pin_input_len > 0)
-               pin_input_len--;
-           }
-         else if (cir_key_is_enter ())
-           {
-             pindisp (' ');
-             chThdExit (0);
-           }
-         else if (pin_input_len < MAX_PIN_CHARS)
-           pin_input_buffer[pin_input_len++] = (uint8_t)cir_data;
+  cirinput_p = cirinput;
 
-         cir_init ();
-       }
+  DEBUG_INFO ("**** CIR data:");
+  DEBUG_WORD (cir_data);
+  if (cir_seq > 48)
+    DEBUG_SHORT (cir_data_more);
+  DEBUG_BYTE (cir_seq);
+#endif
 
-      switch (s++)
+  switch (cir_proto)
+    {
+    case CIR_PROTO_RC5:
+      cir_data &= 0x003f;
+      goto err;
+    case CIR_PROTO_RC6:
+      cir_addr = cir_data >> 8; /* in case of cir_seq == 16.  32??? */
+      cir_data &= 0x00ff;
+      return find_char_codetable (cir_data, cir_codetable_dell_mr425);
+    case CIR_PROTO_NEC:
+      cir_addr = cir_data&0xffff;
+      if (cir_addr == CIR_ADDR_TOSHIBA_REGZA)
+       {
+         cir_data = (cir_data >> 16) & 0x00ff;
+         return find_char_codetable (cir_data, cir_codetable_regza);
+       }
+      else
+       goto err;
+    case CIR_PROTO_SHARP:
+      cir_addr = cir_data&0x0fff;
+      if (cir_addr == CIR_ADDR_SHARP_AQUOS)
+       {
+         cir_data = (cir_data>>16)&0x0fff;
+         return find_char_codetable (cir_data, cir_codetable_aquos);
+       }
+      else
+       goto err;
+    case CIR_PROTO_SONY:
+      /* Remove ADDRESS bits and filter COMMAND bits */
+      if (cir_seq == 1 + 12)
+       {
+         cir_addr = cir_data >> 7;
+         cir_data = cir_data & 0x007f;
+         /* ADDRESS = 0x01 (5-bit) */
+       }
+      else
        {
-       case 0:
-         pindisp ('G');
-         break;
-       case 1:
-         pindisp ('P');
-         break;
-       case 2:
-         pindisp ('G');
-         break;
-       case 3:
-         pindisp ('.');
-         break;
-       default:
-         pindisp (' ');
-         s = 0;
-         break;
+         cir_addr = cir_data >> 8;
+         cir_data = cir_data & 0x00ff;
+         /* ADDRESS = 0x4b or 0x52 (7-bit) */
        }
+      return find_char_codetable (cir_data, cir_codetable_bravia);
+    err:
+    default:
+      /* encode debug information */
+      pin_input_len = 16;
+      pin_input_buffer[0] = hex (cir_proto >> 4);
+      pin_input_buffer[1] = hex (cir_proto & 0x0f);
+      pin_input_buffer[2] = ':';
+      pin_input_buffer[3] = hex ((cir_data >> 28) & 0x0f);
+      pin_input_buffer[4] = hex ((cir_data >> 24) & 0x0f);
+      pin_input_buffer[5] = hex ((cir_data >> 20) & 0x0f);
+      pin_input_buffer[6] = hex ((cir_data >> 16) & 0x0f);
+      pin_input_buffer[7] = hex ((cir_data >> 12) & 0x0f);
+      pin_input_buffer[8] = hex ((cir_data >> 8) & 0x0f);
+      pin_input_buffer[9] = hex ((cir_data >> 4) & 0x0f);
+      pin_input_buffer[10] = hex (cir_data & 0x0f);
+      pin_input_buffer[11] = ':';
+      pin_input_buffer[12] = hex ((cir_data_more >> 12) & 0x0f);
+      pin_input_buffer[13] = hex ((cir_data_more >> 8) & 0x0f);
+      pin_input_buffer[14] = hex ((cir_data_more >> 4) & 0x0f);
+      pin_input_buffer[15] = hex (cir_data_more & 0x0f);
+      return CH_RETURN;
+    }
+}
+
+/*
+ * RC-5 protocol doesn't have a start bit, while any other protocols
+ * have the one.
+ */
+#define CIR_BIT_START_RC5_DETECT 1600 /* RC-5: 889us, Sony start: 2400us */
+
+#define CIR_BIT_START_RC5_LENGTH (889 + 889/2)
+#define CIR_BIT_PERIOD_RC6 444
+#define CIR_BIT_PERIOD 1500
+#define CIR_BIT_SIRC_PERIOD_ON 1000
 
-      chThdSleep (PINDISP_TIMEOUT_INTERVAL0);
+static Thread *pin_thread;
+
+/* 
+ * Let user input PIN string.
+ * Return length of the string.
+ * The string itself is in PIN_INPUT_BUFFER.
+ */
+int
+pinpad_getline (int msg_code, systime_t timeout)
+{
+  (void)msg_code;
+
+  pin_thread = chThdSelf ();
+
+  DEBUG_INFO (">>>\r\n");
+
+  pin_input_len = 0;
+  while (1)
+    {
+      int ch;
+
+      ch = cir_getchar (timeout);
+      if (ch < 0)
+       return 0;               /* timeout */
+
+      if (ch_is_backspace (ch))
+       {
+         led_blink (2);
+         if (pin_input_len > 0)
+           pin_input_len--;
+       }
+      else if (ch_is_enter (ch))
+       break;
+      else if (pin_input_len < MAX_PIN_CHARS)
+       {
+         led_blink (0);
+         pin_input_buffer[pin_input_len++] = ch;
+       }
     }
 
   cir_ext_disable ();
-  return 0;
+
+  return pin_input_len;
 }
 
+/**
+ * @brief  Interrupt handler of EXTI.
+ * @note   This handler will be invoked at the beginning of signal.
+ *         Setup timer to measure period and duty using PWM input mode.
+ */
 void
 cir_ext_interrupt (void)
 {
@@ -340,29 +658,33 @@ cir_ext_interrupt (void)
     }
 #endif
 
-  TIM3->EGR = TIM_EGR_UG;      /* Generate UEV to load PSC and ARR */
+  TIMx->EGR = TIM_EGR_UG;      /* Generate UEV to load PSC and ARR */
   /* Enable Timer */
-  TIM3->SR &= ~(TIM_SR_UIF
+  TIMx->SR &= ~(TIM_SR_UIF
                | TIM_SR_CC1IF | TIM_SR_CC2IF
                | TIM_SR_TIF
                | TIM_SR_CC1OF | TIM_SR_CC2OF);
-  TIM3->DIER = TIM_DIER_UIE /*overflow*/ | TIM_DIER_TIE /*trigger*/;
-  TIM3->CR1 |= TIM_CR1_CEN;
+  TIMx->DIER = TIM_DIER_UIE /*overflow*/ | TIM_DIER_TIE /*trigger*/;
+  TIMx->CR1 |= TIM_CR1_CEN;
 }
 
 #define CIR_PERIOD_ON_RC5_OR_RC6 (((cir_proto == CIR_PROTO_RC5) ? 2 : 1) \
                                  * CIR_BIT_PERIOD_RC6 * 3 / 2)
 
+/**
+ * @brief  Interrupt handler of timer.
+ * @note   Timer is PWM input mode, this handler will be invoked on each cycle
+ */
 void
 cir_timer_interrupt (void)
 {
   uint16_t period, on, off;
 
-  period = TIM3->CCR1;
-  on = TIM3->CCR2;
+  period = TIMx->CCR1;
+  on = TIMx->CCR2;
   off = period - on;
 
-  if ((TIM3->SR & TIM_SR_TIF))
+  if ((TIMx->SR & TIM_SR_TIF))
     {
       if (cir_seq == 0)
        {
@@ -477,23 +799,23 @@ cir_timer_interrupt (void)
       intr_trg++;
 #endif
 
-      TIM3->EGR = TIM_EGR_UG;  /* Generate UEV */
-      TIM3->SR &= ~TIM_SR_TIF;
+      TIMx->EGR = TIM_EGR_UG;  /* Generate UEV */
+      TIMx->SR &= ~TIM_SR_TIF;
     }
   else
     /* overflow occurred */
     {
       systime_t now = chTimeNow ();
 
-      TIM3->SR &= ~TIM_SR_UIF;
+      TIMx->SR &= ~TIM_SR_UIF;
 
       if (on > 0)
        {
          uint8_t ignore_input = 0;
 
          /* Disable the timer */
-         TIM3->CR1 &= ~TIM_CR1_CEN;
-         TIM3->DIER = 0;
+         TIMx->CR1 &= ~TIM_CR1_CEN;
+         TIMx->DIER = 0;
 
          if (cir_seq == 12 || cir_seq == 15)
            {
@@ -536,14 +858,9 @@ cir_timer_interrupt (void)
              cir_input_last = now;
              ignore_input = 1;
            }
-         /* Remove ADDRESS bits and filter COMMAND bits */
          else if (cir_proto == CIR_PROTO_SONY)
            {
-             if (cir_seq == 1 + 12)
-               cir_data = cir_data & 0x007f;
-             else if (cir_seq == 1 + 15)
-               cir_data = cir_data & 0x00ff;
-             else
+             if (cir_seq != 1 + 12 && cir_seq != 1 + 15)
                ignore_input = 1;
            }
          else if (cir_proto == CIR_PROTO_OTHER)
@@ -551,10 +868,7 @@ cir_timer_interrupt (void)
              if (cir_seq == 1 + 32)
                {
                  if (((cir_data >> 16) & 0xff) == ((cir_data >> 24) ^ 0xff))
-                   {
-                     cir_proto = CIR_PROTO_NEC;
-                     cir_data = (cir_data >> 16) & 0x00ff;
-                   }
+                   cir_proto = CIR_PROTO_NEC;
                  else
                    ignore_input = 1;
                }
@@ -569,10 +883,7 @@ cir_timer_interrupt (void)
                       ^ ((cir_data >> 20) & 0x0f) ^ ((cir_data >> 16) & 0x0f)
                       ^ ((cir_data >> 12) & 0x0f) ^ ((cir_data >> 8) & 0x0f)
                       ^ ((cir_data >> 4) & 0x0f) ^ (cir_data & 0x0f)))
-                   {
-                     cir_proto = CIR_PROTO_SHARP;
-                     cir_data = (cir_data >> 16) & 0x0fff;
-                   }
+                   cir_proto = CIR_PROTO_SHARP;
                  else
                    ignore_input = 1;
                }
@@ -581,16 +892,12 @@ cir_timer_interrupt (void)
            }
          else if (cir_proto == CIR_PROTO_RC6)
            {
-             if (cir_seq == 16 || cir_seq == 32)
-               cir_data &= 0x00ff;
-             else
+             if (cir_seq != 16 && cir_seq != 32)
                ignore_input = 1;
            }
          else if (cir_proto == CIR_PROTO_RC5)
            {
-             if (cir_seq == 14)
-               cir_data &= 0x003f;
-             else
+             if (cir_seq != 14)
                ignore_input = 1;
            }
          else
@@ -603,7 +910,8 @@ cir_timer_interrupt (void)
            {
              cir_input_last = now;
              /* Notify thread */
-             chEvtSignal (pin_thread, (eventmask_t)1);
+             if (pin_thread)
+               chEvtSignalI (pin_thread, (eventmask_t)1);
            }
 
 #if defined(DEBUG_CIR)
index 1abef3e..f9b06ef 100644 (file)
@@ -120,8 +120,14 @@ gnuk_device_SetConfiguration (void)
   DEVICE_INFO *pInfo = &Device_Info;
 
   if (pInfo->Current_Configuration != 0)
-    /* Device configured */
-    bDeviceState = CONFIGURED;
+    {                          /* Device configured */
+      extern void *main_thread;
+      extern void chEvtSignalI (void *, unsigned long);
+#define LED_STATUS_MODE   (8)
+
+      bDeviceState = CONFIGURED;
+      chEvtSignalI (main_thread, LED_STATUS_MODE);
+    }
 }
 
 static void