mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
8e475942ad
PD#SWPL-113695 Problem: the length of uevent env string was exceed because of too many keybits. Solution: optimize ir input driver keybit. Verify: t7_an400 Change-Id: Ib1292c65fdd7444d4158d46dbd3b36fefdeffebb Signed-off-by: Zelong Dong <zelong.dong@amlogic.com>
192 lines
4.5 KiB
C
192 lines
4.5 KiB
C
// 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;
|
|
}
|
|
|
|
meson_ir_input_configure(chip->r_dev, &ir_map->tab);
|
|
/*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);
|
|
|