blob: d7bd149180fbdca0659d005d8aec676f9ced9326 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include "meson_ir_main.h"
#include "meson_ir_sysfs.h"
#define AML_REMOTE_NAME "amremote"
static int meson_ir_open(struct inode *inode, struct file *file)
{
struct meson_ir_chip *chip;
chip = container_of(inode->i_cdev, struct meson_ir_chip, chrdev);
file->private_data = chip;
disable_irq(chip->irqno);
return 0;
}
static long meson_ir_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)file->private_data;
struct ir_sw_decode_para sw_data;
struct meson_ir_map_tab_list *ir_map;
struct meson_ir_map_tab_list *ptable;
void __user *parg = (void __user *)arg;
unsigned long flags;
u32 value;
int retval = 0;
if (!parg) {
dev_err(chip->dev, "%s invalid user space pointer\n", __func__);
return -EINVAL;
}
mutex_lock(&chip->file_lock);
switch (cmd) {
case IR_IOC_GET_DATA_VERSION:
if (copy_to_user(parg, SHARE_DATA_VERSION,
sizeof(SHARE_DATA_VERSION))) {
retval = -EFAULT;
goto err;
}
break;
case IR_IOC_SET_KEY_NUMBER:
if (copy_from_user(&value, parg, sizeof(u32))) {
chip->key_num.update_flag = false;
retval = -EFAULT;
goto err;
}
chip->key_num.update_flag = true;
chip->key_num.value = value;
break;
case IR_IOC_SET_KEY_MAPPING_TAB:
if (chip->key_num.update_flag) {
ir_map = kzalloc(sizeof(*ir_map) + chip->key_num.value *
sizeof(union _codemap),
GFP_KERNEL);
if (!ir_map) {
dev_err(chip->dev, "%s ir map table alloc err\n",
__func__);
retval = -ENOMEM;
goto err;
}
if (copy_from_user(&ir_map->tab, parg,
sizeof(struct ir_map_tab) +
chip->key_num.value * sizeof(union _codemap))) {
meson_ir_tab_free(ir_map);
retval = -EFAULT;
goto err;
}
/* Check data whether valid or not*/
if (chip->key_num.value != ir_map->tab.map_size) {
meson_ir_tab_free(ir_map);
retval = -EFAULT;
goto err;
}
/*scancode sort*/
meson_ir_scancode_sort(&ir_map->tab);
/*overwrite the old map table or insert new map table*/
spin_lock_irqsave(&chip->slock, flags);
ptable = meson_ir_seek_map_tab(chip,
ir_map->tab.custom_code);
if (ptable) {
if (ptable == chip->cur_tab)
chip->cur_tab = ir_map;
list_del(&ptable->list);
meson_ir_tab_free(ptable);
}
list_add_tail(&ir_map->list, &chip->map_tab_head);
spin_unlock_irqrestore(&chip->slock, flags);
chip->key_num.update_flag = false;
}
break;
case IR_IOC_SET_SW_DECODE_PARA:
if (copy_from_user(&sw_data, parg,
sizeof(struct ir_sw_decode_para))) {
retval = -EFAULT;
goto err;
}
chip->r_dev->max_frame_time = sw_data.max_frame_time;
break;
default:
retval = -ENOTTY;
goto err;
}
err:
mutex_unlock(&chip->file_lock);
return retval;
}
static int meson_ir_release(struct inode *inode, struct file *file)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)file->private_data;
enable_irq(chip->irqno);
file->private_data = NULL;
return 0;
}
static const struct file_operations meson_ir_fops = {
.owner = THIS_MODULE,
.open = meson_ir_open,
#ifdef CONFIG_COMPAT
.compat_ioctl = meson_ir_ioctl,
#endif
.unlocked_ioctl = meson_ir_ioctl,
.release = meson_ir_release,
};
int meson_ir_cdev_init(struct meson_ir_chip *chip)
{
int ret = 0;
chip->dev_name = AML_REMOTE_NAME;
ret = alloc_chrdev_region(&chip->chr_devno,
0, 1, AML_REMOTE_NAME);
if (ret < 0) {
dev_err(chip->dev, "failed to allocate major number\n");
return ret;
}
cdev_init(&chip->chrdev, &meson_ir_fops);
chip->chrdev.owner = THIS_MODULE;
ret = cdev_add(&chip->chrdev, chip->chr_devno, 1);
if (ret < 0) {
dev_err(chip->dev, "failed to cdev_add\n");
goto err_cdev_add;
}
ret = meson_ir_sysfs_init(chip);
if (ret < 0) {
dev_err(chip->dev, "failed to ir_sys create %d\n", ret);
goto err_ir_sys;
}
return 0;
err_ir_sys:
cdev_del(&chip->chrdev);
err_cdev_add:
unregister_chrdev_region(chip->chr_devno, 1);
return ret;
}
EXPORT_SYMBOL(meson_ir_cdev_init);
void meson_ir_cdev_free(struct meson_ir_chip *chip)
{
meson_ir_sysfs_exit(chip);
cdev_del(&chip->chrdev);
unregister_chrdev_region(chip->chr_devno, 1);
}
EXPORT_SYMBOL(meson_ir_cdev_free);