diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 6cbafb79583..663dc27ec9e 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -2,56 +2,53 @@ #include #include -#include #include #include #include -#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2) -#define INPUT_PRESS_TICKS 150 -#define INPUT_LONG_PRESS_COUNTS 2 -#define INPUT_THREAD_FLAG_ISR 0x00000001 +#define TAG "Input" -/** Input pin state */ -typedef struct { - const InputPin* pin; - // State - volatile bool state; - volatile uint8_t debounce; - FuriTimer* press_timer; - FuriPubSub* event_pubsub; - volatile uint8_t press_counter; - volatile uint32_t counter; -} InputPinState; +#define INPUT_SRV_DEBOUNCE_TIMER_TICKS 1 //ms -/** Input CLI command handler */ -void input_cli(Cli* cli, FuriString* args, void* context); - -// #define INPUT_DEBUG +#define INPUT_SRV_INPUT_LONG_PRESS_TICKS 150 //ms +#define INPUT_SRV_LONG_PRESS_COUNTS 2 #define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted)) -void input_press_timer_callback(void* arg) { - InputPinState* input_pin = arg; - InputEvent event; - event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE; - event.sequence_counter = input_pin->counter; - event.key = input_pin->pin->key; - input_pin->press_counter++; - if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { - event.type = InputTypeLong; - furi_pubsub_publish(input_pin->event_pubsub, &event); - } else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) { - input_pin->press_counter--; - event.type = InputTypeRepeat; - furi_pubsub_publish(input_pin->event_pubsub, &event); - } -} +#ifdef INPUT_DEBUG +#define INPUT_LOG(...) FURI_LOG_D(TAG, __VA_ARGS__) +#else +#define INPUT_LOG(...) +#endif -void input_isr(void* _ctx) { - FuriThreadId thread_id = (FuriThreadId)_ctx; - furi_thread_flags_set(thread_id, INPUT_THREAD_FLAG_ISR); -} +typedef struct { + FuriEventLoopTimer* timer; + FuriPubSub* event_pubsub; + uint32_t sequence_counter; + uint32_t press_counter; + InputKey key; +} InputSrvKeySequence; + +typedef struct { + const InputPin* pin; + uint16_t debounce_count; + bool state; +} InputSrvKeyState; + +typedef struct { + FuriEventLoop* event_loop; + FuriPubSub* event_pubsub; + FuriSemaphore* input_semaphore; + FuriEventLoopTimer* debounce_timer; + InputSrvKeyState* key_state; + InputSrvKeySequence* key_sequence; + uint32_t sequence_counter; +} InputSrv; + +static void input_key_sequence_run( + InputSrvKeySequence* key_sequence, + InputType type, + uint32_t sequence_counter); const char* input_get_key_name(InputKey key) { for(size_t i = 0; i < input_pins_count; i++) { @@ -59,7 +56,7 @@ const char* input_get_key_name(InputKey key) { return input_pins[i].name; } } - return "Unknown"; + furi_crash(); } const char* input_get_type_name(InputType type) { @@ -75,95 +72,187 @@ const char* input_get_type_name(InputType type) { case InputTypeRepeat: return "Repeat"; default: - return "Unknown"; + furi_crash(); } } -int32_t input_srv(void* p) { - UNUSED(p); - - const FuriThreadId thread_id = furi_thread_get_current_id(); - FuriPubSub* event_pubsub = furi_pubsub_alloc(); - uint32_t counter = 1; - furi_record_create(RECORD_INPUT_EVENTS, event_pubsub); +static void input_isr_key(void* context) { + InputSrv* instance = context; + furi_semaphore_release(instance->input_semaphore); +} -#ifdef INPUT_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); -#endif +static void input_semaphore_callback(FuriEventLoopObject* object, void* context) { + furi_assert(context); + InputSrv* instance = context; + furi_assert(object == instance->input_semaphore); -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub); -#endif + furi_check(furi_semaphore_acquire(instance->input_semaphore, 0) == FuriStatusOk); - InputPinState pin_states[input_pins_count]; + if(!furi_event_loop_timer_is_running(instance->debounce_timer)) { + furi_event_loop_timer_start(instance->debounce_timer, INPUT_SRV_DEBOUNCE_TIMER_TICKS); + } +} +static void input_debounce_timer_callback(void* context) { + furi_assert(context); + InputSrv* instance = context; + bool is_changing = false; for(size_t i = 0; i < input_pins_count; i++) { - furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr, thread_id); - pin_states[i].pin = &input_pins[i]; - pin_states[i].state = GPIO_Read(pin_states[i]); - pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF; - pin_states[i].press_timer = - furi_timer_alloc(input_press_timer_callback, FuriTimerTypePeriodic, &pin_states[i]); - pin_states[i].event_pubsub = event_pubsub; - pin_states[i].press_counter = 0; - } + bool state = GPIO_Read(instance->key_state[i]); - while(1) { - bool is_changing = false; - for(size_t i = 0; i < input_pins_count; i++) { - bool state = GPIO_Read(pin_states[i]); - if(state) { - if(pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) pin_states[i].debounce += 1; - } else { - if(pin_states[i].debounce > 0) pin_states[i].debounce -= 1; + if(state) { + if(instance->key_state[i].debounce_count < INPUT_DEBOUNCE_TICKS) { + instance->key_state[i].debounce_count++; + is_changing = true; } + } else if(instance->key_state[i].debounce_count > 0) { + instance->key_state[i].debounce_count--; + is_changing = true; + } - if(pin_states[i].debounce > 0 && pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { - is_changing = true; - } else if(pin_states[i].state != state) { - pin_states[i].state = state; - - // Common state info - InputEvent event; - event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE; - event.key = pin_states[i].pin->key; - - // Short / Long / Repeat timer routine - if(state) { - pin_states[i].counter = counter++; - event.sequence_counter = pin_states[i].counter; - furi_timer_start(pin_states[i].press_timer, INPUT_PRESS_TICKS); - } else { - event.sequence_counter = pin_states[i].counter; - furi_timer_stop(pin_states[i].press_timer); - while(furi_timer_is_running(pin_states[i].press_timer)) - furi_delay_tick(1); - if(pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { - event.type = InputTypeShort; - furi_pubsub_publish(event_pubsub, &event); - } - pin_states[i].press_counter = 0; - } - - // Send Press/Release event - event.type = pin_states[i].state ? InputTypePress : InputTypeRelease; - furi_pubsub_publish(event_pubsub, &event); + if(!is_changing && instance->key_state[i].state != state) { + instance->key_state[i].state = state; + + if(state) { + input_key_sequence_run( + &instance->key_sequence[i], InputTypePress, ++instance->sequence_counter); + } else { + input_key_sequence_run(&instance->key_sequence[i], InputTypeRelease, 0); } } + } - if(is_changing) { -#ifdef INPUT_DEBUG - furi_hal_gpio_write(&gpio_ext_pa4, 1); -#endif - furi_delay_tick(1); - } else { -#ifdef INPUT_DEBUG - furi_hal_gpio_write(&gpio_ext_pa4, 0); -#endif - furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever); + if(!is_changing) { + furi_event_loop_timer_stop(instance->debounce_timer); + } +} + +static inline void + input_send(FuriPubSub* pubsub, InputKey key, InputType type, uint32_t sequence_counter) { + InputEvent event = { + .sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE, + .sequence_counter = sequence_counter, + .key = key, + .type = type, + }; + + furi_pubsub_publish(pubsub, &event); + INPUT_LOG( + "input_send: %s %s %x", + input_get_key_name(event.key), + input_get_type_name(event.type), + event.sequence_counter); +} + +static void input_key_sequence_run( + InputSrvKeySequence* key_sequence, + InputType type, + uint32_t sequence_counter) { + switch(type) { + case InputTypePress: + key_sequence->sequence_counter = sequence_counter; + key_sequence->press_counter = 0; + + furi_assert(!furi_event_loop_timer_is_running(key_sequence->timer)); + furi_event_loop_timer_start(key_sequence->timer, INPUT_SRV_INPUT_LONG_PRESS_TICKS); + + input_send( + key_sequence->event_pubsub, + key_sequence->key, + InputTypePress, + key_sequence->sequence_counter); + + break; + case InputTypeRelease: + if(key_sequence->press_counter < INPUT_SRV_LONG_PRESS_COUNTS) { + input_send( + key_sequence->event_pubsub, + key_sequence->key, + InputTypeShort, + key_sequence->sequence_counter); } + + furi_assert(furi_event_loop_timer_is_running(key_sequence->timer)); + furi_event_loop_timer_stop(key_sequence->timer); + + input_send( + key_sequence->event_pubsub, + key_sequence->key, + InputTypeRelease, + key_sequence->sequence_counter); + + break; + default: + furi_crash(); + break; } +} + +static void input_sequence_timer_callback(void* context) { + furi_assert(context); + InputSrvKeySequence* key_sequence = context; + + key_sequence->press_counter++; + + if(key_sequence->press_counter == INPUT_SRV_LONG_PRESS_COUNTS) { + input_send( + key_sequence->event_pubsub, + key_sequence->key, + InputTypeLong, + key_sequence->sequence_counter); + } else if(key_sequence->press_counter > INPUT_SRV_LONG_PRESS_COUNTS) { + input_send( + key_sequence->event_pubsub, + key_sequence->key, + InputTypeRepeat, + key_sequence->sequence_counter); + } +} + +int32_t input_srv(void* p) { + UNUSED(p); + InputSrv* instance = malloc(sizeof(InputSrv)); + instance->event_pubsub = furi_pubsub_alloc(); + furi_record_create(RECORD_INPUT_EVENTS, instance->event_pubsub); + + instance->input_semaphore = furi_semaphore_alloc(1, 0); + instance->event_loop = furi_event_loop_alloc(); + instance->debounce_timer = furi_event_loop_timer_alloc( + instance->event_loop, + input_debounce_timer_callback, + FuriEventLoopTimerTypePeriodic, + instance); + + instance->key_state = malloc(sizeof(InputSrvKeyState) * input_pins_count); + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr_key, instance); + instance->key_state[i].pin = &input_pins[i]; + instance->key_state[i].state = GPIO_Read(instance->key_state[i]); + instance->sequence_counter = 0; + } + + furi_event_loop_subscribe_semaphore( + instance->event_loop, + instance->input_semaphore, + FuriEventLoopEventIn, + input_semaphore_callback, + instance); + + instance->key_sequence = malloc(sizeof(InputSrvKeySequence) * input_pins_count); + for(size_t i = 0; i < input_pins_count; i++) { + instance->key_sequence[i].sequence_counter = 0; + instance->key_sequence[i].press_counter = 0; + instance->key_sequence[i].key = input_pins[i].key; + instance->key_sequence[i].timer = furi_event_loop_timer_alloc( + instance->event_loop, + input_sequence_timer_callback, + FuriEventLoopTimerTypePeriodic, + &instance->key_sequence[i]); + instance->key_sequence[i].event_pubsub = instance->event_pubsub; + } + + // Start Input Service + furi_event_loop_run(instance->event_loop); return 0; }