Files

658 lines
21 KiB
C

/*
Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd.
*/
#include "ot_ir.h"
#include "ot_osal.h"
#include "ot_ir_codedef.h"
#include "ot_ir_hal.h"
#ifndef NULL
#define NULL ((void *)0)
#endif
/* irq */
static uintptr_t g_ir_irq = 0;
/* io */
#define IR_REG_LENGTH 0x1000
static volatile void *g_ir_reg_base = NULL;
#define IR_ENABLE ((uintptr_t)g_ir_reg_base + 0x00)
#define IR_CONFIG ((uintptr_t)g_ir_reg_base + 0x04)
#define CNT_LEADS ((uintptr_t)g_ir_reg_base + 0x08)
#define CNT_LEADE ((uintptr_t)g_ir_reg_base + 0x0c)
#define CNT_SLEADE ((uintptr_t)g_ir_reg_base + 0x10)
#define CNT0_B ((uintptr_t)g_ir_reg_base + 0x14)
#define CNT1_B ((uintptr_t)g_ir_reg_base + 0x18)
#define IR_BUSY ((uintptr_t)g_ir_reg_base + 0x1c)
#define IR_DATAH ((uintptr_t)g_ir_reg_base + 0x20)
#define IR_DATAL ((uintptr_t)g_ir_reg_base + 0x24)
#define IR_INTM ((uintptr_t)g_ir_reg_base + 0x28)
#define IR_INTS ((uintptr_t)g_ir_reg_base + 0x2c)
#define IR_INTC ((uintptr_t)g_ir_reg_base + 0x30)
#define IR_START ((uintptr_t)g_ir_reg_base + 0x34)
/* interrupt mask */
#define INTMS_RCV (1L << 16)
#define INTMS_FRAMERR (1L << 17)
#define INTMS_OVERFLOW (1L << 18)
#define INTMS_RELEASE (1L << 19)
#define INT_MASK (INTMS_RCV | INTMS_FRAMERR | INTMS_OVERFLOW | INTMS_RELEASE)
/* define macro */
#define write_reg(addr, value) ((*(volatile unsigned int *)(addr)) = (value))
#define read_reg(addr) (*(volatile unsigned int *)(addr))
/* debug */
static int g_dbg_flag = 0;
#define OT_IR_PFX "ot_ir: "
#define ot_ir_dbg(params...) \
do { \
if (g_dbg_flag) { \
osal_printk(params); \
} \
} while (0)
/* unit is ms */
#define DEFAULT_DELAYTIME (200 * 10)
#define OT_IR_MAX_BUF 100
#define DEFAULT_BUF_LEN 8
#define BUF_HEAD g_ot_ir_dev.buf[g_ot_ir_dev.head]
#define BUF_TAIL g_ot_ir_dev.buf[g_ot_ir_dev.tail]
#define BUF_LAST g_ot_ir_dev.buf[(g_ot_ir_dev.head == 0) ? (g_ot_ir_dev.buf_len - 1) : (g_ot_ir_dev.head - 1)]
typedef struct {
ot_ir_dev_param dev_parm;
unsigned int head;
unsigned int tail;
irkey_info_s buf[OT_IR_MAX_BUF + 1]; /* 1:fifo reserve */
unsigned int buf_len;
unsigned int enable_repkey;
unsigned int repkey_checkflag;
unsigned int repkey_delaytime; /* ms */
unsigned int enable_keyup;
osal_wait_t ir_key_wait;
} ot_ir_dev_struct;
static ot_ir_dev_struct g_ot_ir_dev;
static void repkey_timeout_handler(unsigned long data);
static struct osal_timer g_repkey_timeout_timer;
static int g_need_iounmap = 0;
uintptr_t *get_ir_irq(void)
{
return &g_ir_irq;
}
volatile void **get_ir_reg_base(void)
{
return &g_ir_reg_base;
}
static void ot_ir_show_reg(void)
{
if (g_dbg_flag) {
osal_printk("OT_IR11 REG:###################################################\n");
osal_printk("%.8x ", read_reg(IR_ENABLE));
osal_printk("%.8x ", read_reg(IR_CONFIG));
osal_printk("%.8x ", read_reg(CNT_LEADS));
osal_printk("%.8x ", read_reg(CNT_LEADE));
osal_printk("%.8x ", read_reg(CNT_SLEADE));
osal_printk("%.8x ", read_reg(CNT0_B));
osal_printk("%.8x ", read_reg(CNT1_B));
osal_printk("\n");
osal_printk("%.8x ", read_reg(IR_BUSY));
osal_printk("%.8x ", read_reg(IR_DATAH));
osal_printk("%.8x ", read_reg(IR_DATAL));
osal_printk("%.8x ", read_reg(IR_INTM));
osal_printk("%.8x ", read_reg(IR_INTS));
osal_printk("%.8x ", read_reg(IR_INTC));
osal_printk("%.8x ", read_reg(IR_START));
osal_printk("\n");
}
}
static inline unsigned int inc_buf(unsigned int x, unsigned int len)
{
++x;
if (len == 0) {
return x % DEFAULT_BUF_LEN;
} else {
return x % len;
}
}
static void write_reg32(unsigned int value, unsigned int mask, const void *addr)
{
unsigned int t;
t = osal_readl((void *)addr);
t &= ~mask;
t |= value & mask;
osal_writel(t, (void *)addr);
}
static int ot_ir_enable_clock(void)
{
void *ir_crg_addr = NULL;
ir_crg_addr = osal_ioremap(IR_CRG_ADDR, (unsigned long)IR_CLOCK_REG_LENGTH);
if (ir_crg_addr == NULL) {
return -1;
}
#ifdef IR_IS_DEFAULT_RESET
write_reg32(0x0, 0x1, ir_crg_addr); /* 0x0: [1] bit for clock reset */
#endif
write_reg32(0x1 << IR_BIT, 0x1 << IR_BIT, ir_crg_addr); /* 0x1: [1] bit for clock control */
osal_iounmap((void *)ir_crg_addr);
return 0;
}
static void ot_ir_disable_clock(void)
{
void *ir_crg_addr = NULL;
ir_crg_addr = osal_ioremap(IR_CRG_ADDR, (unsigned long)IR_CLOCK_REG_LENGTH);
if (ir_crg_addr == NULL) {
return;
}
write_reg32(0x0 << IR_BIT, 0x1 << IR_BIT, ir_crg_addr); /* 0x1: [1] bit for clock control */
osal_iounmap((void *)ir_crg_addr);
}
static void ot_ir_config(void)
{
unsigned int value;
write_reg(IR_ENABLE, 0x01); /* 0x01: for IR control */
while (read_reg(IR_BUSY)) {
ot_ir_dbg("IR_BUSY. Wait...\n");
}
value = (g_ot_ir_dev.dev_parm.codetype << 14); /* 14: [15:14] bits for code type */
value |= (g_ot_ir_dev.dev_parm.code_len - 1) << 8; /* 8: [13:8] bits for code len */
value |= (g_ot_ir_dev.dev_parm.frequence - 1); /* [6:0] bits for frequence */
write_reg(IR_CONFIG, value);
value = g_ot_ir_dev.dev_parm.leads_min << 16; /* 16: [25:16] bits for min leads */
value |= g_ot_ir_dev.dev_parm.leads_max; /* [9:0] bits for max leads */
write_reg(CNT_LEADS, value);
value = g_ot_ir_dev.dev_parm.leade_min << 16; /* 16, bits [24:16] for min leads */
value |= g_ot_ir_dev.dev_parm.leade_max; /* [8:0] bits for max leads */
write_reg(CNT_LEADE, value);
value = g_ot_ir_dev.dev_parm.sleade_min << 16; /* 16, bits [24:16] for min sleade */
value |= g_ot_ir_dev.dev_parm.sleade_max; /* [8:0] bits for max sleade */
write_reg(CNT_SLEADE, value);
value = g_ot_ir_dev.dev_parm.cnt0_b_min << 16; /* 16, bits [24:16] for min cnt0 */
value |= g_ot_ir_dev.dev_parm.cnt0_b_max; /* [8:0] bits for max cnt0 */
write_reg(CNT0_B, value);
value = g_ot_ir_dev.dev_parm.cnt1_b_min << 16; /* 16, bits [24:16] for min cnt1 */
value |= g_ot_ir_dev.dev_parm.cnt1_b_max; /* [8:0] bits for max cnt1 */
write_reg(CNT1_B, value);
write_reg(IR_INTM, 0x00);
write_reg(IR_START, 0x00);
}
static void repkey_timeout_handler(unsigned long data)
{
ot_ir_unused(data);
osal_del_timer(&g_repkey_timeout_timer);
g_ot_ir_dev.repkey_checkflag = 0;
}
static void interrupt_rcv(void)
{
ot_ir_dbg("receive data interrupt. 0x%.8x-0x%.8x\n", read_reg(IR_DATAH), read_reg(IR_DATAL));
write_reg(IR_INTC, 0x01 << 0); /* 0x01: clear interrupt */
if (g_ot_ir_dev.enable_repkey) {
if ((g_ot_ir_dev.repkey_checkflag) &&
(BUF_LAST.irkey_datah == read_reg(IR_DATAH)) &&
(BUF_LAST.irkey_datal == read_reg(IR_DATAL))) {
ot_ir_dbg("repeart key [0x%.8x]-[0x%.8x] detective\n", read_reg(IR_DATAH), read_reg(IR_DATAL));
} else {
/* repeat key check */
osal_del_timer(&g_repkey_timeout_timer);
osal_set_timer(&g_repkey_timeout_timer, g_ot_ir_dev.repkey_delaytime / 10); /* 10: delaytime means ms*10 */
g_ot_ir_dev.repkey_checkflag = 1;
BUF_HEAD.irkey_datah = read_reg(IR_DATAH);
BUF_HEAD.irkey_datal = read_reg(IR_DATAL);
BUF_HEAD.irkey_state_code = OT_IR_KEY_DOWN;
g_ot_ir_dev.head = inc_buf(g_ot_ir_dev.head, g_ot_ir_dev.buf_len);
osal_wakeup(&(g_ot_ir_dev.ir_key_wait));
}
} else if (!((BUF_LAST.irkey_datah == read_reg(IR_DATAH)) &&
(BUF_LAST.irkey_datal == read_reg(IR_DATAL)) &&
(BUF_LAST.irkey_state_code == OT_IR_KEY_DOWN))) {
BUF_HEAD.irkey_datah = read_reg(IR_DATAH);
BUF_HEAD.irkey_datal = read_reg(IR_DATAL);
BUF_HEAD.irkey_state_code = OT_IR_KEY_DOWN;
g_ot_ir_dev.head = inc_buf(g_ot_ir_dev.head, g_ot_ir_dev.buf_len);
osal_wakeup(&(g_ot_ir_dev.ir_key_wait));
}
}
static int ot_ir_interrupt(int irq, void *dev_id)
{
unsigned int int_state = read_reg(IR_INTS);
ot_ir_unused(irq);
ot_ir_unused(dev_id);
if ((int_state & INT_MASK) == 0) {
return OSAL_IRQ_NONE;
}
ot_ir_dbg("Enter ot_ir_interrupt.\n");
ot_ir_show_reg();
if (int_state & INTMS_FRAMERR) {
ot_ir_dbg("frame error.\n");
write_reg(IR_INTC, 0x01 << 1); /* 1: write 0x01 to [1] bit, clear frame error interrupt */
} else if (int_state & INTMS_OVERFLOW) {
ot_ir_dbg("hardware overflow.\n");
write_reg(IR_INTC, 0x01 << 2); /* 2: write 0x01 to [2] bit, clear hardware overflow interrupt */
} else if (int_state & INTMS_RELEASE) {
ot_ir_dbg("release key interrupt.\n");
write_reg(IR_INTC, 0x01 << 3); /* 3: write 0x01 to [3] bit, clear release key interrupt */
osal_del_timer(&g_repkey_timeout_timer);
g_ot_ir_dev.repkey_checkflag = 0;
if ((g_ot_ir_dev.enable_keyup) && (BUF_LAST.irkey_state_code != OT_IR_KEY_UP)) {
BUF_HEAD = BUF_LAST;
BUF_HEAD.irkey_state_code = OT_IR_KEY_UP;
g_ot_ir_dev.head = inc_buf(g_ot_ir_dev.head, g_ot_ir_dev.buf_len);
osal_wakeup(&(g_ot_ir_dev.ir_key_wait));
}
} else if (int_state & INTMS_RCV) {
interrupt_rcv();
} else {
osal_printk("logic Error: int_mask=0x%.8x, int_state=0x%.8x.\n", read_reg(IR_INTM), int_state);
}
ot_ir_dbg("Exit ot_ir_interrupt.\n");
return OSAL_IRQ_HANDLED;
}
static long ot_ir_ioctl(unsigned int cmd, unsigned long arg, void *private_data)
{
int *p = (int *)(uintptr_t)arg;
unsigned int min_val, max_val;
ot_ir_unused(private_data);
switch (cmd) {
case IR_IOC_SET_BUF:
if ((arg > 0) && (arg <= OT_IR_MAX_BUF)) {
g_ot_ir_dev.buf_len = arg + 1;
/* need reset buf pointer */
g_ot_ir_dev.head = 0;
g_ot_ir_dev.tail = 0;
}
ot_ir_dbg("IR_IOC_SET_BUF->buf_len=%u\n", g_ot_ir_dev.buf_len);
break;
case IR_IOC_SET_ENABLE_KEYUP:
g_ot_ir_dev.enable_keyup = arg;
ot_ir_dbg("IR_IOC_SET_ENABLE_KEYUP->enable_keyup=%u\n", g_ot_ir_dev.enable_keyup);
break;
case IR_IOC_SET_ENABLE_REPKEY:
g_ot_ir_dev.enable_repkey = arg;
ot_ir_dbg("IR_IOC_SET_ENABLE_REPKEY->enable_repkey=%u\n", g_ot_ir_dev.enable_repkey);
break;
case IR_IOC_SET_REPKEY_TIMEOUTVAL:
if (arg > 0) {
g_ot_ir_dev.repkey_delaytime = arg;
}
ot_ir_dbg("IR_IOC_SET_REPKEY_TIMEOUTVAL->repkey_delaytime=%u\n", g_ot_ir_dev.repkey_delaytime);
break;
case IR_IOC_SET_FORMAT:
if (arg > 3) { /* format:[0,3] */
osal_printk("Error: IR_IOC_SET_FORMAT->invalid args=%lu\n", arg);
return -1;
}
g_ot_ir_dev.dev_parm.codetype = arg;
ot_ir_dbg("IR_IOC_SET_FORMAT->codetype=%u\n", g_ot_ir_dev.dev_parm.codetype);
break;
case IR_IOC_SET_CODELEN:
g_ot_ir_dev.dev_parm.code_len = arg;
ot_ir_dbg("IR_IOC_SET_CODELEN->code_len=%u\n", g_ot_ir_dev.dev_parm.code_len);
break;
case IR_IOC_SET_FREQ:
if ((arg <= 0) || (arg > 128)) { /* 128:max freq */
osal_printk("Error: IR_IOC_SET_FREQ->invalid args=%lu\n", arg);
return -1;
}
g_ot_ir_dev.dev_parm.frequence = arg;
ot_ir_dbg("IR_IOC_SET_FREQ->frequence=%u\n", g_ot_ir_dev.dev_parm.frequence);
break;
case IR_IOC_SET_LEADS:
if (osal_copy_from_user(&min_val, p, sizeof(unsigned int)) ||
osal_copy_from_user(&max_val, p + 1, sizeof(unsigned int))) {
return -1;
}
g_ot_ir_dev.dev_parm.leads_min = min_val;
g_ot_ir_dev.dev_parm.leads_max = max_val;
ot_ir_dbg("IR_IOC_SET_LEADS->leads_min=%u, leads_max=%u\n",
g_ot_ir_dev.dev_parm.leads_min, g_ot_ir_dev.dev_parm.leads_max);
break;
case IR_IOC_SET_LEADE:
if (osal_copy_from_user(&min_val, p, sizeof(unsigned int)) ||
osal_copy_from_user(&max_val, p + 1, sizeof(unsigned int))) {
return -1;
}
g_ot_ir_dev.dev_parm.leade_min = min_val;
g_ot_ir_dev.dev_parm.leade_max = max_val;
ot_ir_dbg("IR_IOC_SET_LEADE->leade_min=%u, leade_max=%u\n",
g_ot_ir_dev.dev_parm.leade_min, g_ot_ir_dev.dev_parm.leade_max);
break;
case IR_IOC_SET_SLEADE:
if (osal_copy_from_user(&min_val, p, sizeof(unsigned int)) ||
osal_copy_from_user(&max_val, p + 1, sizeof(unsigned int))) {
return -1;
}
g_ot_ir_dev.dev_parm.sleade_min = min_val;
g_ot_ir_dev.dev_parm.sleade_max = max_val;
ot_ir_dbg("IR_IOC_SET_SLEADE->sleade_min=%u, sleade_max=%u\n",
g_ot_ir_dev.dev_parm.sleade_min, g_ot_ir_dev.dev_parm.sleade_max);
break;
case IR_IOC_SET_CNT0_B:
if (osal_copy_from_user(&min_val, p, sizeof(unsigned int)) ||
osal_copy_from_user(&max_val, p + 1, sizeof(unsigned int))) {
return -1;
}
g_ot_ir_dev.dev_parm.cnt0_b_min = min_val;
g_ot_ir_dev.dev_parm.cnt0_b_max = max_val;
ot_ir_dbg("IR_IOC_SET_CNT0_B->cnt0_b_min=%u, cnt0_b_max=%u\n",
g_ot_ir_dev.dev_parm.cnt0_b_min, g_ot_ir_dev.dev_parm.cnt0_b_max);
break;
case IR_IOC_SET_CNT1_B:
if (osal_copy_from_user(&min_val, p, sizeof(unsigned int)) ||
osal_copy_from_user(&max_val, p + 1, sizeof(unsigned int))) {
return -1;
}
g_ot_ir_dev.dev_parm.cnt1_b_min = min_val;
g_ot_ir_dev.dev_parm.cnt1_b_max = max_val;
ot_ir_dbg("IR_IOC_SET_CNT1_B->cnt1_b_min=%u, cnt1_b_max=%u\n",
g_ot_ir_dev.dev_parm.cnt1_b_min, g_ot_ir_dev.dev_parm.cnt1_b_max);
break;
case IR_IOC_GET_CONFIG:
if (osal_copy_to_user((void *)(uintptr_t)arg, &(g_ot_ir_dev.dev_parm), sizeof(ot_ir_dev_param))) {
osal_printk("%s: copy_to_user fail!\n", __func__);
}
ot_ir_dbg("IR_IOC_GET_CONFIG\n");
break;
case IR_IOC_ENDBG:
g_dbg_flag = 1;
ot_ir_dbg("IR_IOC_ENDBG\n");
break;
case IR_IOC_DISDBG:
g_dbg_flag = 0;
ot_ir_dbg("IR_IOC_DISDBG\n");
break;
default:
osal_printk("Error: Inappropriate ioctl for device. cmd=%u\n", cmd);
return -1;
}
ot_ir_config();
return 0;
}
static int ot_ir_wait_condition_callback(const void *param)
{
ot_ir_unused(param);
return (g_ot_ir_dev.head != g_ot_ir_dev.tail);
}
static int ot_ir_read(char *buf, int count, long *f_pos, void *private_data)
{
irkey_info_s irkey_to_user;
int res = 0;
ot_ir_unused(f_pos);
ot_ir_unused(private_data);
retry :
ot_ir_dbg("Enter ot_ir_read : head=%u, tail=%u, buf_len=%u\n",
g_ot_ir_dev.head, g_ot_ir_dev.tail, g_ot_ir_dev.buf_len);
if ((g_ot_ir_dev.head) == (g_ot_ir_dev.tail)) {
res = osal_wait_event_interruptible(&(g_ot_ir_dev.ir_key_wait), ot_ir_wait_condition_callback, &g_ot_ir_dev);
if (res != 0) {
return res;
}
goto retry;
} else {
while (((g_ot_ir_dev.head) != (g_ot_ir_dev.tail)) && ((res + (int)sizeof(irkey_info_s)) <= count)) {
irkey_to_user = BUF_TAIL;
g_ot_ir_dev.tail = inc_buf(g_ot_ir_dev.tail, g_ot_ir_dev.buf_len);
if (osal_copy_to_user((buf + res), &irkey_to_user, sizeof(irkey_info_s))) {
osal_printk("%s: copy_to_user fail!\n", __func__);
}
res += sizeof(irkey_info_s);
}
}
return res;
}
static unsigned int ot_ir_select(osal_poll_t *osal_poll, void *private_data)
{
ot_ir_unused(private_data);
ot_ir_dbg("Enter ot_ir_select.\n");
if ((g_ot_ir_dev.head) != (g_ot_ir_dev.tail)) {
return 1;
}
osal_poll_wait(osal_poll, &(g_ot_ir_dev.ir_key_wait));
return 0;
}
static osal_atomic_t g_ot_ir_s_available;
static int ot_ir_open(void *private_data)
{
int ret;
ot_ir_unused(private_data);
ot_ir_dbg("Enter ot_ir_open.\n");
if (osal_atomic_dec_return(&g_ot_ir_s_available) != 0) {
ret = osal_atomic_inc_return(&g_ot_ir_s_available);
osal_printk("Error: device already open:%d.\n", ret);
return -1;
}
osal_del_timer(&g_repkey_timeout_timer);
g_dbg_flag = 0;
/* init g_ot_ir_dev */
g_ot_ir_dev.dev_parm = g_static_dev_param[0]; /* nec format */
g_ot_ir_dev.head = 0;
g_ot_ir_dev.tail = 0;
g_ot_ir_dev.buf_len = DEFAULT_BUF_LEN;
g_ot_ir_dev.enable_repkey = 1;
g_ot_ir_dev.repkey_checkflag = 0;
g_ot_ir_dev.repkey_delaytime = DEFAULT_DELAYTIME;
g_ot_ir_dev.enable_keyup = 1;
osal_wait_init(&g_ot_ir_dev.ir_key_wait);
ot_ir_config();
return 0;
}
static int ot_ir_release(void *private_data)
{
ot_ir_unused(private_data);
ot_ir_dbg("Enter ot_ir_release.\n");
/* disable OT_IR11 */
write_reg(IR_ENABLE, 0x0);
if (osal_atomic_inc_return(&g_ot_ir_s_available) != 1) {
osal_atomic_dec_return(&g_ot_ir_s_available);
return 0;
}
osal_del_timer(&g_repkey_timeout_timer);
osal_wait_destroy(&g_ot_ir_dev.ir_key_wait);
return 0;
}
static int ot_ir_write(const char *buf, int size, long *offset, void *private_data)
{
ot_ir_unused(buf);
ot_ir_unused(size);
ot_ir_unused(offset);
ot_ir_unused(private_data);
return 0;
}
static struct osal_fileops g_ot_ir_fops = {
open : ot_ir_open,
unlocked_ioctl : ot_ir_ioctl,
poll : ot_ir_select,
read : ot_ir_read,
write : ot_ir_write,
release : ot_ir_release,
};
static struct osal_dev *g_ot_ir_miscdev = NULL;
static int ot_ir_sw_init(void)
{
int res;
if (g_ir_irq <= 0) {
g_ir_irq = OT_IR_DEVICE_IRQ_NO;
}
if (g_ir_reg_base == NULL) {
g_ir_reg_base = (volatile void *)osal_ioremap(IR_REG_BASE, IR_REG_LENGTH);
if (g_ir_reg_base == NULL) {
osal_printk("osal_ioremap err. \n");
return 1;
}
g_need_iounmap = 1;
}
res = osal_atomic_init(&g_ot_ir_s_available);
if (res != 0) {
osal_printk("Error: init atomic\n");
if (g_need_iounmap) {
osal_iounmap((void *)g_ir_reg_base);
g_need_iounmap = 0;
g_ir_reg_base = NULL;
}
return 1;
}
osal_atomic_set(&g_ot_ir_s_available, 1);
g_repkey_timeout_timer.function = repkey_timeout_handler;
res = osal_timer_init(&g_repkey_timeout_timer);
if (res != 0) {
if (g_need_iounmap) {
osal_iounmap((void *)g_ir_reg_base);
g_need_iounmap = 0;
g_ir_reg_base = NULL;
}
osal_atomic_destroy(&g_ot_ir_s_available);
return 1;
}
return 0;
}
int ot_ir_init(void)
{
if (ot_ir_sw_init() != 0) {
return -1;
}
if (ot_ir_enable_clock() != 0) {
osal_printk("Error: enable clock fail\n");
goto enable_clock_fail;
}
/* disable OT_IR */
write_reg(IR_ENABLE, 0x00);
g_ot_ir_miscdev = osal_createdev(OT_IR_DEVICE_NAME);
if (g_ot_ir_miscdev == NULL) {
osal_printk("Error: can't create dev\n");
goto create_dev_fail;
}
g_ot_ir_miscdev->minor = 255; /* 255:dev_minor */
g_ot_ir_miscdev->fops = &g_ot_ir_fops;
if (osal_registerdevice(g_ot_ir_miscdev) != 0) {
osal_printk("Error: can't register\n");
goto register_device_fail;
}
if (osal_request_irq(g_ir_irq, ot_ir_interrupt, NULL, OT_IR_DEVICE_NAME, &ot_ir_interrupt) != 0) {
osal_printk("Error: request IRQ(%lu) failed\n", g_ir_irq);
goto request_irq_fail;
}
osal_printk("ot_ir init ok. ver=%s, %s.\n", __DATE__, __TIME__);
return 0;
request_irq_fail:
osal_deregisterdevice(g_ot_ir_miscdev);
register_device_fail:
osal_destroydev(g_ot_ir_miscdev);
create_dev_fail:
ot_ir_disable_clock();
enable_clock_fail:
osal_timer_destroy(&g_repkey_timeout_timer);
osal_atomic_destroy(&g_ot_ir_s_available);
if (g_need_iounmap) {
osal_iounmap((void *)g_ir_reg_base);
g_need_iounmap = 0;
g_ir_reg_base = NULL;
}
return -1;
}
void ot_ir_exit(void)
{
osal_free_irq(g_ir_irq, &ot_ir_interrupt);
osal_deregisterdevice(g_ot_ir_miscdev);
osal_destroydev(g_ot_ir_miscdev);
osal_del_timer(&g_repkey_timeout_timer);
osal_timer_destroy(&g_repkey_timeout_timer);
osal_atomic_destroy(&g_ot_ir_s_available);
if (g_need_iounmap) {
osal_iounmap((void *)g_ir_reg_base);
g_need_iounmap = 0;
g_ir_reg_base = NULL;
}
ot_ir_disable_clock();
osal_printk("ot_ir exit ok.\n");
}