#define pr_fmt(fmt) "[hui][generic]" fmt #include "hui_internal.h" #include "hui_generic.h" #include #include #include #include #include #include #include #include #define ON 1 #define ON_125ms 1 #define ON_250ms 1, 1 #define ON_500ms 1, 1, 1, 1 #define ON_1s 1, 1, 1, 1, 1, 1, 1, 1 #define OFF 0 #define OFF_125ms 0 #define OFF_250ms 0, 0 #define OFF_500ms 0, 0, 0, 0 #define OFF_1s 0, 0, 0, 0, 0, 0, 0, 0 /* Each blink1/blink2 has to end with one of the following states, else the * state machine will not work */ /* If blink1 'stays' in blink1 there is no need to define blink2 */ #define GOTO_BLINK1 2 #define REPEAT_GOTO_BLINK2 3 #define GOTO_BLINK2 4 #define REPEAT_GOTO_BLINK1 5 #define END 6 struct led_blink_code { const int8_t *blink1; const int8_t *blink2; }; const int8_t code_einschalten_blink1[] = { ON, END }; const struct led_blink_code code_einschalten = { .blink1 = code_einschalten_blink1, }; /*--- DEFINE STATE ausschalten OFF(), END ; ---*/ const int8_t code_ausschalten_blink1[] = { OFF, END }; const struct led_blink_code code_ausschalten = { .blink1 = code_ausschalten_blink1, }; /*--- DEFINE STATE keine_action END ;; ---*/ const int8_t code_keine_action_blink1[] = { END }; const struct led_blink_code code_keine_action = { .blink1 = code_keine_action_blink1, }; /*--- DEFINE STATE 0_5HZ_binking start:, ON(), WAIT(1000) , OFF(), WAIT(1000), * LOOP TO start, END ; ---*/ const int8_t code_0_5HZ_blinking_blink1[] = { ON_1s, OFF_1s, GOTO_BLINK1 }; const struct led_blink_code code_0_5HZ_blinking = { .blink1 = code_0_5HZ_blinking_blink1, }; /*--- DEFINE STATE 1HZ_binking start:, ON(), WAIT(500) , OFF(), WAIT(500), LOOP * TO start, END ; ---*/ const int8_t code_1HZ_blinking_blink1[] = { ON_500ms, OFF_500ms, GOTO_BLINK1 }; const struct led_blink_code code_1HZ_blinking = { .blink1 = code_1HZ_blinking_blink1, }; /*--- DEFINE STATE 2HZ_binking start:, ON(), WAIT(250) , OFF(), WAIT(250), LOOP * TO start, END ; ---*/ const int8_t code_2HZ_blinking_blink1[] = { ON_250ms, OFF_250ms, GOTO_BLINK1 }; const struct led_blink_code code_2HZ_blinking = { .blink1 = code_2HZ_blinking_blink1, }; /*--- DEFINE STATE 4HZ_binking start:, ON(), WAIT(125) , OFF(), WAIT(125), LOOP * TO start, END ; ---*/ const int8_t code_4HZ_blinking_blink1[] = { ON_125ms, OFF_125ms, GOTO_BLINK1 }; const struct led_blink_code code_4HZ_blinking = { .blink1 = code_4HZ_blinking_blink1, }; /* DEFINE STATE hardware_error_blink start:, RELOAD PARAM 1, loop1:, ON(HARDWARE_ERROR_LED1), ON(HARDWARE_ERROR_LED2), ON(HARDWARE_ERROR_LED3), ON(HARDWARE_ERROR_LED4), ON(HARDWARE_ERROR_LED5), ON(HARDWARE_ERROR_LED6), ON(HARDWARE_ERROR_LED7), ON(HARDWARE_ERROR_LED8), WAIT(250), OFF(HARDWARE_ERROR_LED1), OFF(HARDWARE_ERROR_LED2), OFF(HARDWARE_ERROR_LED3), OFF(HARDWARE_ERROR_LED4), OFF(HARDWARE_ERROR_LED5), OFF(HARDWARE_ERROR_LED6), OFF(HARDWARE_ERROR_LED7), OFF(HARDWARE_ERROR_LED8), WAIT(250), PARAM 1 --, LOOP TO loop1 IF PARAM 1, WAIT(750), LOOP TO start, END ; */ const struct led_blink_code code_hardware_error_blinking = { .blink1 = (const int8_t[]){ ON_250ms, OFF_250ms, REPEAT_GOTO_BLINK2 }, .blink2 = (const int8_t[]){ OFF_500ms, OFF_250ms, GOTO_BLINK1 }, }; const int8_t code_double_blink_code_2HZ_blink1[] = { ON_250ms, OFF_250ms, ON_250ms, OFF_250ms, OFF_500ms, REPEAT_GOTO_BLINK2 }; const int8_t code_double_blink_code_2HZ_blink2[] = { OFF_1s, GOTO_BLINK1 }; const struct led_blink_code code_double_blink_code_2HZ = { .blink1 = code_double_blink_code_2HZ_blink1, .blink2 = code_double_blink_code_2HZ_blink2, }; const int8_t code_einschalten_off_on_blink1[] = { OFF_250ms, ON_250ms, END }; const struct led_blink_code code_einschalten_off_on = { .blink1 = code_einschalten_off_on_blink1, }; /** * @brief The blink codes are stored in this array. The access is done via the * @ref LED_BLINK_CODE_DEFINES * * When changing this table be aware of the order. * The order has to match the #defines in led_controller.h */ const struct led_blink_code *blink_codes[] = { &code_ausschalten, /*--- #define LED_OFF 0 ---*/ &code_einschalten, /*--- #define LED_ON 1 ---*/ &code_0_5HZ_blinking, /*--- #define LED_0_5HZ 2 ---*/ &code_1HZ_blinking, /*--- #define LED_1HZ 3 ---*/ &code_2HZ_blinking, /*--- #define LED_2HZ 4 ---*/ &code_4HZ_blinking, /*--- #define LED_4HZ 5 ---*/ &code_hardware_error_blinking, /*--- 6 ---*/ &code_double_blink_code_2HZ, /*--- 7 ---*/ &code_einschalten_off_on, /*--- 8 ---*/ }; static inline void blink_handle_timer(timer_callback j); static HUI_DEFINE_TIMER(blink_timer, blink_handle_timer); static struct task_struct *blink_thread; static LIST_HEAD(all_leds); static LIST_HEAD(all_buttons); static inline void blink_handle_timer(timer_callback k) { mod_timer(&blink_timer, blink_timer.expires + msecs_to_jiffies(125)); wake_up_process(blink_thread); } static inline void blink_thread_one(void) { struct hui_generic_led *led; struct hui_generic_button *button; // First move through all, to update the engine list_for_each_entry (led, &all_leds, engine.list) { /* check if 125 ms have passed --> we can advance in the blink * _code */ const int8_t *blink12; /* store a pointer to the blink1/blink2 array depending on * blink1_blink2 */ if (led->engine.blink1_blink2 == 0) { blink12 = blink_codes[led->engine._code]->blink1; } else { blink12 = blink_codes[led->engine._code]->blink2; } /* we only treat the on off here */ if ((blink12[led->engine.current_blink_step] == ON) || (blink12[led->engine.current_blink_step] == OFF)) { led->engine.on_off = blink12[led->engine.current_blink_step]; /* advance in blink _code */ led->engine.current_blink_step++; } /* all looping and switching states are treated here */ if (blink12[led->engine.current_blink_step] == GOTO_BLINK1) { led->engine.current_blink_step = 0; led->engine.blink1_blink2 = 0; led->engine.param_cnt = led->engine.param1; } else if (blink12[led->engine.current_blink_step] == REPEAT_GOTO_BLINK2) { led->engine.current_blink_step = 0; /* repeat blink1 param1 times */ if (led->engine.param_cnt) { led->engine.param_cnt--; } /* goto blink2 */ if (led->engine.param_cnt == 0) { led->engine.param_cnt = led->engine.param2; led->engine.blink1_blink2 = 1; } } else if (blink12[led->engine.current_blink_step] == GOTO_BLINK2) { led->engine.current_blink_step = 0; led->engine.blink1_blink2 = 1; led->engine.param_cnt = led->engine.param2; } else if (blink12[led->engine.current_blink_step] == REPEAT_GOTO_BLINK1) { led->engine.current_blink_step = 0; /* repeat blink2 param2 times */ if (led->engine.param_cnt) { led->engine.param_cnt--; } /* goto blink0 */ if (led->engine.param_cnt == 0) { led->engine.param_cnt = led->engine.param1; led->engine.blink1_blink2 = 0; } } } // Second, move through all to output values list_for_each_entry (led, &all_leds, engine.list) { struct led_color color = { 0 }; int ret = 0; if (led->engine.on_off) color = led->code.color; // Sadly, some low-level gpio handlers are really expensive, // therefore we avoid calling them with the same value multiple // times. if (!led_color_equals(led->engine.last_color, color)) ret = led->out(led, color); // Only store if setting was successful if (!ret) led->engine.last_color = color; } list_for_each_entry (button, &all_buttons, engine.list) { int state = button->in(button); if (button->engine.state == state) continue; avm_hui_button_input(button->handle, state > 0); button->engine.state = state; } } static int blink_thread_func(void *unused) { while (!kthread_should_stop()) { blink_thread_one(); // Wait for next loop set_current_state(TASK_INTERRUPTIBLE); schedule(); } return 0; } static void hui_generic_led_handler(struct blink_code code, void *ctx) { struct hui_generic_led *led = ctx; struct hui_generic_led *other; if (blink_code_equals(led->code, code)) return; pr_debug("Set code %d (%d, %d; %02x%02x%02x; %02x) for <%s>\n", code.type, code.params[0], code.params[1], code.color.c[0], code.color.c[1], code.color.c[2], code.color.brightness, led->name); led->engine.param1 = code.params[0]; led->engine.param2 = code.params[1]; led->engine._code = code.type; /* If only brightness changed, do not reset current position in blink pattern. * This fixes interrupted blink patterns during ambient light dimming. */ if (!blink_code_except_brightness_equals(led->code, code)) { led->engine.param_cnt = 0; led->engine.current_blink_step = 0; led->engine.blink1_blink2 = 0; } led->code = code; /* make blinking per state synchronized, find led with same code and * param and copy values */ list_for_each_entry (other, &all_leds, engine.list) { if (led == other) continue; if (other->engine._code == led->engine._code && other->engine.param1 == led->engine.param1 && other->engine.param2 == led->engine.param2) { led->engine.on_off = other->engine.on_off; led->engine.param_cnt = other->engine.param_cnt; led->engine.current_blink_step = other->engine.current_blink_step; led->engine.blink1_blink2 = other->engine.blink1_blink2; break; } } } static void raw_out(struct led_color color, void *ctx) { struct hui_generic_led *led = ctx; led->out(led, color); } struct avm_hui_led_ops hui_generic_ops = { .out = hui_generic_led_handler, .raw_out = raw_out, }; static bool is_running(void) { return !list_empty(&all_leds) || !list_empty(&all_buttons); } static void generic_blink_start(void) { blink_thread = kthread_create(blink_thread_func, NULL, "hui_generic"); if (IS_ERR(blink_thread)) return; pr_debug("Start blink timer\n"); mod_timer(&blink_timer, jiffies + msecs_to_jiffies(125)); } int hui_generic_blink_add_led(struct hui_generic_led *led) { int running = is_running(); /* * Initialize with odd values to trigger at least one call at the * beginning. * * Brightness zero and other components set should not happen normally. */ led->engine.last_color.brightness = 0x0; led->engine.last_color.c[0] = 0xFF; led->engine.last_color.c[1] = 0xFF; led->engine.last_color.c[2] = 0xFF; list_add_tail(&led->engine.list, &all_leds); if (!running && is_running()) generic_blink_start(); return 0; } int hui_generic_blink_add_button(struct hui_generic_button *button) { int running = is_running(); list_add_tail(&button->engine.list, &all_buttons); if (!running && is_running()) generic_blink_start(); return 0; }