| /** |
| ******************************************************************************* |
| * |
| * @file rc_ir.c |
| * |
| * @brief IR application part |
| * |
| * Copyright (C) Atmosic 2020-2021 |
| * |
| ******************************************************************************* |
| */ |
| |
| #include "arch.h" |
| #include <inttypes.h> |
| #include "rc_ir.h" |
| #include "ir_ctl.h" |
| #include "timer.h" |
| #include "sw_event.h" |
| #include "sw_timer.h" |
| #include "atm_pm.h" |
| #include "co_list.h" |
| #ifdef CFG_ATVRC_UNI_IR |
| #include "bridge_ir.h" |
| #endif |
| |
| |
| #ifndef CFG_RC_IR_QUEUE_MAX |
| #define CFG_RC_IR_QUEUE_MAX 10 |
| #endif |
| |
| static struct { |
| co_list_t keys; |
| pm_lock_id_t lock_hib; |
| sw_timer_id_t tid; |
| sw_event_id_t eid; |
| uint8_t count; |
| bool sent; |
| bool pressed; |
| #ifdef CFG_ATVRC_UNI_IR |
| bool is_uni; |
| uint16_t last_uni_key; |
| uint32_t last_uni_sent; |
| #endif |
| } ir_ctx; |
| |
| typedef struct { |
| co_list_hdr_t hdr; |
| uint32_t address; |
| uint32_t cmd; |
| #ifdef CFG_ATVRC_UNI_IR |
| uint16_t uni_key; |
| uint8_t const *uni_code; |
| uint16_t uni_size; |
| #endif |
| uint32_t delay_cs; |
| } ir_key_t; |
| |
| static void rc_ir_dispatch(void) |
| { |
| ir_key_t const *key = (ir_key_t const *)co_list_pick(&ir_ctx.keys); |
| if (key) { |
| atm_pm_lock(ir_ctx.lock_hib); |
| sw_timer_set(ir_ctx.tid, key->delay_cs); |
| } else if (!sw_timer_active(ir_ctx.tid)) { |
| // Notify upper layer if needed. |
| atm_pm_unlock(ir_ctx.lock_hib); |
| } |
| } |
| |
| static void sw_event_func(sw_event_id_t event_id, void const *ctx) |
| { |
| sw_event_clear(event_id); |
| rc_ir_dispatch(); |
| } |
| |
| // __FAST because called from interrupt handler. |
| __FAST |
| static void rc_ir_end(void) |
| { |
| sw_event_set(ir_ctx.eid); |
| } |
| |
| static void rc_ir_key_pop_out(void) |
| { |
| ir_key_t *key = (ir_key_t *)co_list_pop_front(&ir_ctx.keys); |
| if (key) { |
| ir_ctx.count--; |
| sw_timer_clear(ir_ctx.tid); |
| free(key); |
| } |
| } |
| |
| #ifdef CFG_NEC_IR_OVERRIDE |
| #include "rc_keycode.h" |
| #include "nvds.h" |
| #define OVRD_ENTRY_NUM 48 |
| #define OVRD_ENTRY_LEN 4 |
| static uint8_t const ovrd_ir_cmd_map[OVRD_ENTRY_NUM] = { |
| IR_POWER, IR_RIGHT, IR_VOLD, IR_INFO, IR_NUM4, IR_GREEN, IR_INPUT, IR_DOWN, |
| IR_CNLD, IR_NUM0, IR_NUM3, IR_RED, IR_BKMK, IR_BACK, IR_YOUTUBE, IR_SUBT, |
| IR_NUM2, IR_NUM6, IR_ASST, IR_HOME, IR_NETFLIX, IR_NUM9, IR_NUM1, IR_NUM5, |
| IR_DASHB, IR_GUIDE, IR_APP03, IR_NUM8, IR_BLUE, 0, IR_UP, IR_VOLU, IR_APP04, |
| IR_NUM7, IR_YELLOW, 0, IR_LEFT, IR_CNLU, 0, 0, 0, 0, IR_CENTER, IR_MUTE, |
| 0, 0, 0, 0, |
| }; |
| static uint8_t ovrd_val[OVRD_ENTRY_NUM][OVRD_ENTRY_LEN]; |
| static void nec_ir_override_init(void) |
| { |
| nvds_tag_len_t len = sizeof(ovrd_val); |
| #define NVDS_TAG_ATVRC_NEC_IR_OVERRIDE 0xC2 |
| if (nvds_get(NVDS_TAG_ATVRC_NEC_IR_OVERRIDE, &len, (uint8_t*)&ovrd_val) |
| != NVDS_OK) { |
| memset(ovrd_val, 0xFF, sizeof(ovrd_val)); |
| } |
| } |
| |
| static bool nec_ir_override_check(ir_data_t *ir_data) |
| { |
| bool is_ovrd = false; |
| for (uint8_t oft = 0; oft < OVRD_ENTRY_NUM; oft++) { |
| if (ir_data->cmd == ovrd_ir_cmd_map[oft]) { |
| uint32_t *data = (uint32_t*)ir_data; |
| for (uint8_t idx = 0; idx < OVRD_ENTRY_LEN; idx++) { |
| if (ovrd_val[oft][idx] != 0xFF) { |
| *(data + idx) = ovrd_val[oft][idx]; |
| is_ovrd = true; |
| } |
| } |
| return is_ovrd; |
| } |
| } |
| return is_ovrd; |
| } |
| #endif |
| |
| static void rc_ir_send_msg_id(sw_timer_id_t idx, void const *ctx) |
| { |
| ir_key_t const *key = (ir_key_t const *)co_list_pick(&ir_ctx.keys); |
| if (key) { // prevent earlier end |
| ir_ctx.sent = true; |
| #ifdef CFG_ATVRC_UNI_IR |
| if (key->uni_code) { |
| ir_ctx.is_uni = true; |
| ir_ctx.last_uni_key = key->uni_key; |
| ir_ctx.last_uni_sent = atm_get_sys_time(); |
| uni_ir_send(key->uni_code, key->uni_size); |
| rc_ir_key_pop_out(); |
| return; |
| } |
| #endif |
| DEBUG_TRACE("send %#" PRIx32 " %#" PRIx32, key->address, key->cmd); |
| bool repeat = ir_ctx.pressed && (ir_ctx.count == 1); |
| #ifndef IR_DATA_CONST |
| #define IR_DATA_CONST const |
| #endif |
| ir_data_t IR_DATA_CONST data = { |
| .addr = key->address, |
| .inv_addr = ~key->address, |
| .cmd = key->cmd, |
| .inv_cmd = ~key->cmd |
| }; |
| #ifdef CFG_NEC_IR_OVERRIDE |
| if (nec_ir_override_check(&data)) { |
| DEBUG_TRACE("override %#" PRIx32 " %#" PRIx32 " %#" PRIx32 |
| " %#" PRIx32, data.addr, data.inv_addr, data.cmd, data.inv_cmd); |
| } |
| #endif |
| nec_ir_send(&data, repeat); |
| rc_ir_key_pop_out(); |
| } |
| } |
| |
| void rc_ir_init(void) |
| { |
| ir_ctl_init(rc_ir_end); |
| ir_ctx.eid = sw_event_alloc(sw_event_func, NULL); |
| #ifdef CFG_NEC_IR_OVERRIDE |
| nec_ir_override_init(); |
| #endif |
| ir_ctx.tid = sw_timer_alloc(rc_ir_send_msg_id, &ir_ctx); |
| co_list_init(&ir_ctx.keys); |
| ir_ctx.lock_hib = atm_pm_alloc(PM_LOCK_HIBERNATE); |
| } |
| |
| void rc_ir_send(uint32_t address, uint32_t cmd, uint32_t delay_cs) |
| { |
| if (!address || (ir_ctx.count >= CFG_RC_IR_QUEUE_MAX)) { |
| return; |
| } |
| |
| ir_key_t *ikey = malloc(sizeof(ir_key_t)); |
| if (!ikey) { |
| return; |
| } |
| |
| ikey->address = address; |
| ikey->cmd = cmd; |
| ikey->delay_cs = delay_cs; |
| |
| #ifdef CFG_ATVRC_UNI_IR |
| ikey->uni_code = NULL; |
| #endif |
| |
| co_list_push_back(&ir_ctx.keys, &ikey->hdr); |
| |
| if (!ir_ctx.count++) { |
| rc_ir_dispatch(); |
| } |
| ir_ctx.pressed = true; |
| } |
| |
| void rc_ir_repeat_end(void) |
| { |
| ir_ctx.pressed = false; |
| |
| if (ir_ctx.sent) { |
| #ifdef CFG_ATVRC_UNI_IR |
| if(ir_ctx.is_uni) { |
| bridge_ir_key_release(); |
| ir_ctx.is_uni = false; |
| } |
| #endif |
| // this will trigger callback |
| ir_ctl_stop_rept(); |
| ir_ctx.sent = false; |
| } else { |
| rc_ir_dispatch(); |
| } |
| } |
| |
| #ifdef CFG_ATVRC_UNI_IR |
| bool rc_ir_check_uni_key(uint16_t atv_keycode) |
| { |
| return bridge_ir_check_key(atv_keycode); |
| } |
| |
| void rc_ir_send_uni_code(uint16_t key, uint8_t const *code, uint16_t size) |
| { |
| if (!code || (ir_ctx.count >= CFG_RC_IR_QUEUE_MAX)) { |
| return; |
| } |
| |
| ir_key_t *ikey = malloc(sizeof(ir_key_t)); |
| if (!ikey) { |
| return; |
| } |
| |
| ikey->uni_key = key; |
| ikey->uni_code = code; |
| ikey->uni_size = size; |
| ikey->delay_cs = 6; |
| |
| if (ir_ctx.last_uni_key == key) { |
| uint32_t delay = uni_ir_get_rept_delay(code); |
| uint32_t delta_time = atm_get_sys_time() - ir_ctx.last_uni_sent; |
| if (delay && (atm_lpc_to_us(delta_time) < delay)) { |
| DEBUG_TRACE("repeat delay(%lu) > delta(%lu)", delay, delta_time); |
| ikey->delay_cs = delay / US_PER_CS; |
| } |
| } |
| |
| co_list_push_back(&ir_ctx.keys, &ikey->hdr); |
| |
| if (!ir_ctx.count++) { |
| rc_ir_dispatch(); |
| } |
| } |
| |
| void rc_ir_clear_uni_codes(void) |
| { |
| bridge_ir_clear(); |
| } |
| #endif |