blob: f8c490ccb621692a94a74ca7b9ea53bad02c6c0c [file] [log] [blame]
/**
*******************************************************************************
*
* @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