kernel_samsung_a53x/drivers/input/sec_input/stm/stm_mmap.c
2024-06-15 16:02:09 -03:00

660 lines
16 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "stm_dev.h"
#ifdef RAWDATA_MMAP
#include "stm_reg.h"
#include <linux/miscdevice.h>
struct tsp_ioctl {
int num;
u8 data[PAGE_SIZE];
};
#define IOCTL_TSP_MAP_READ _IOR(0, 0, struct tsp_ioctl)
static long tsp_ioctl_handler(struct file *file, unsigned int cmd, void __user *p, int compat_mode)
{
static struct tsp_ioctl t;
int num;
if (copy_from_user(&t, p, sizeof(struct tsp_ioctl))) {
input_err(true, &g_ts->client->dev, "%s: failed to copyt_from_user\n", __func__);
return -EFAULT;
}
if (cmd == IOCTL_TSP_MAP_READ) {
#if 0
input_info(true, &g_ts->client->dev, "%s: num: %d\n", __func__, t.num);
#endif
num = t.num % 5;
if (num == 0) {
memcpy(&t.data, g_ts->raw_v0, g_ts->raw_len);
if (copy_to_user(p, (void *)&t, sizeof(struct tsp_ioctl))) {
input_err(true, &g_ts->client->dev, "%s: failed to copyt_to_user\n", __func__);
return -EFAULT;
}
} else if (num == 1) {
memcpy(&t.data, g_ts->raw_v1, g_ts->raw_len);
if (copy_to_user(p, (void *)&t, sizeof(struct tsp_ioctl))) {
input_err(true, &g_ts->client->dev, "%s: failed to copyt_to_user\n", __func__);
return -EFAULT;
}
} else if (num == 2) {
memcpy(&t.data, g_ts->raw_v2, g_ts->raw_len);
if (copy_to_user(p, (void *)&t, sizeof(struct tsp_ioctl))) {
input_err(true, &g_ts->client->dev, "%s: failed to copyt_to_user\n", __func__);
return -EFAULT;
}
} else if (num == 3) {
memcpy(&t.data, g_ts->raw_v3, g_ts->raw_len);
if (copy_to_user(p, (void *)&t, sizeof(struct tsp_ioctl))) {
input_err(true, &g_ts->client->dev, "%s: failed to copyt_to_user\n", __func__);
return -EFAULT;
}
} else if (num == 4) {
memcpy(&t.data, g_ts->raw_v4, g_ts->raw_len);
if (copy_to_user(p, (void *)&t, sizeof(struct tsp_ioctl))) {
input_err(true, &g_ts->client->dev, "%s: failed to copyt_to_user\n", __func__);
return -EFAULT;
}
}
}
return 0;
}
static long tsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return tsp_ioctl_handler(file, cmd, (void __user *)arg, 0);
}
static int tsp_open(struct inode *inode, struct file *file)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int tsp_close(struct inode *inode, struct file *file)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static const struct file_operations tsp_io_fos = {
.owner = THIS_MODULE,
.unlocked_ioctl = tsp_ioctl,
.open = tsp_open,
.release = tsp_close,
};
static const struct miscdevice tsp_misc = {
.fops = &tsp_io_fos,
.minor = MISC_DYNAMIC_MINOR,
.name = "tspio",
};
MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
void tsp_ioctl_init(void)
{
int ret;
ret = misc_register(&tsp_misc);
input_info(true, &g_ts->client->dev, "%s: ret: %d\n", __func__, ret);
}
/* tsp rawdata : using mmap */
#define DEV_NAME "tsp_data"
#define DEV_NAME0 "tsp_data0"
#define DEV_NAME1 "tsp_data1"
#define DEV_NAME2 "tsp_data2"
#define DEV_NAME3 "tsp_data3"
#define DEV_NAME4 "tsp_data4"
#define DATA_SIZE (2 * PAGE_SIZE)
static const unsigned int MINOR_BASE;
static const unsigned int MINOR_NUM0 = 1;
static const unsigned int MINOR_NUM1 = 2;
static const unsigned int MINOR_NUM2 = 3;
static const unsigned int MINOR_NUM3 = 4;
static const unsigned int MINOR_NUM4 = 5;
static struct cdev *mmapdev_cdev0;
static struct cdev *mmapdev_cdev1;
static struct cdev *mmapdev_cdev2;
static struct cdev *mmapdev_cdev3;
static struct cdev *mmapdev_cdev4;
static struct class *mmapdev_class;
static dev_t dev0;
static dev_t dev1;
static dev_t dev2;
static dev_t dev3;
static dev_t dev4;
/* mmap0 */
static vm_fault_t mmap0_vm_fault(struct vm_fault *vmf)
{
struct page *page = NULL;
unsigned long offset = 0;
void *page_ptr = NULL;
input_info(true, &g_ts->client->dev, "%s\n", __func__);
if (vmf == NULL)
return VM_FAULT_SIGBUS;
offset = vmf->address - vmf->vma->vm_start;
if (offset >= DATA_SIZE)
return VM_FAULT_SIGBUS;
page_ptr = g_ts->raw_v0 + offset;
page = vmalloc_to_page(page_ptr);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct vma0_ops = {
.fault = mmap0_vm_fault
};
static int mmap0_open(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap0_release(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap0_remap(struct file *filp, struct vm_area_struct *vma)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &vma0_ops;
return 0;
}
static const struct file_operations mmap0_fops = {
.open = mmap0_open,
.release = mmap0_release,
.mmap = mmap0_remap,
};
/* map1 */
static vm_fault_t mmap1_vm_fault(struct vm_fault *vmf)
{
struct page *page = NULL;
unsigned long offset = 0;
void *page_ptr = NULL;
input_info(true, &g_ts->client->dev, "%s\n", __func__);
if (vmf == NULL)
return VM_FAULT_SIGBUS;
offset = vmf->address - vmf->vma->vm_start;
if (offset >= DATA_SIZE)
return VM_FAULT_SIGBUS;
page_ptr = g_ts->raw_v1 + offset;
page = vmalloc_to_page(page_ptr);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct vma1_ops = {
.fault = mmap1_vm_fault
};
static int mmap1_open(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap1_release(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap1_remap(struct file *filp, struct vm_area_struct *vma)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &vma1_ops;
return 0;
}
static const struct file_operations mmap1_fops = {
.open = mmap1_open,
.release = mmap1_release,
.mmap = mmap1_remap,
};
/* map2 */
static vm_fault_t mmap2_vm_fault(struct vm_fault *vmf)
{
struct page *page = NULL;
unsigned long offset = 0;
void *page_ptr = NULL;
input_info(true, &g_ts->client->dev, "%s\n", __func__);
if (vmf == NULL)
return VM_FAULT_SIGBUS;
offset = vmf->address - vmf->vma->vm_start;
if (offset >= DATA_SIZE)
return VM_FAULT_SIGBUS;
page_ptr = g_ts->raw_v2 + offset;
page = vmalloc_to_page(page_ptr);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct vma2_ops = {
.fault = mmap2_vm_fault
};
static int mmap2_open(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap2_release(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap2_remap(struct file *filp, struct vm_area_struct *vma)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &vma2_ops;
return 0;
}
static const struct file_operations mmap2_fops = {
.open = mmap2_open,
.release = mmap2_release,
.mmap = mmap2_remap,
};
/* map3 */
static vm_fault_t mmap3_vm_fault(struct vm_fault *vmf)
{
struct page *page = NULL;
unsigned long offset = 0;
void *page_ptr = NULL;
input_info(true, &g_ts->client->dev, "%s\n", __func__);
if (vmf == NULL)
return VM_FAULT_SIGBUS;
offset = vmf->address - vmf->vma->vm_start;
if (offset >= DATA_SIZE)
return VM_FAULT_SIGBUS;
page_ptr = g_ts->raw_v3 + offset;
page = vmalloc_to_page(page_ptr);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct vma3_ops = {
.fault = mmap3_vm_fault
};
static int mmap3_open(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap3_release(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap3_remap(struct file *filp, struct vm_area_struct *vma)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &vma3_ops;
return 0;
}
static const struct file_operations mmap3_fops = {
.open = mmap3_open,
.release = mmap3_release,
.mmap = mmap3_remap,
};
/* map4 */
static vm_fault_t mmap4_vm_fault(struct vm_fault *vmf)
{
struct page *page = NULL;
unsigned long offset = 0;
void *page_ptr = NULL;
input_info(true, &g_ts->client->dev, "%s\n", __func__);
if (vmf == NULL)
return VM_FAULT_SIGBUS;
offset = vmf->address - vmf->vma->vm_start;
if (offset >= DATA_SIZE)
return VM_FAULT_SIGBUS;
page_ptr = g_ts->raw_v4 + offset;
page = vmalloc_to_page(page_ptr);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct vma4_ops = {
.fault = mmap4_vm_fault
};
static int mmap4_open(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap4_release(struct inode *inode, struct file *filp)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
return 0;
}
static int mmap4_remap(struct file *filp, struct vm_area_struct *vma)
{
input_info(true, &g_ts->client->dev, "%s\n", __func__);
vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &vma4_ops;
return 0;
}
static const struct file_operations mmap4_fops = {
.open = mmap4_open,
.release = mmap4_release,
.mmap = mmap4_remap,
};
static ssize_t raw_irq_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sec_cmd_data *sec = dev_get_drvdata(dev);
struct stm_ts_data *ts = container_of(sec, struct stm_ts_data, sec);
ssize_t ret;
mutex_lock(&ts->raw_lock);
ret = snprintf(buf, PAGE_SIZE, "%d,%d", ts->before_irq_count, ts->raw_irq_count);
mutex_unlock(&ts->raw_lock);
return ret;
}
static DEVICE_ATTR_RO(raw_irq);
static struct attribute *rawdata_attrs[] = {
&dev_attr_raw_irq.attr,
NULL,
};
static struct attribute_group rawdata_attr_group = {
.attrs = rawdata_attrs,
};
int stm_ts_rawdata_buffer_alloc(struct stm_ts_data *ts)
{
ts->raw_v0 = vmalloc(ts->raw_len);
if (!ts->raw_v0)
goto alloc_out;
ts->raw_v1 = vmalloc(ts->raw_len);
if (!ts->raw_v1)
goto alloc_out;
ts->raw_v2 = vmalloc(ts->raw_len);
if (!ts->raw_v2)
goto alloc_out;
ts->raw_v3 = vmalloc(ts->raw_len);
if (!ts->raw_v3)
goto alloc_out;
ts->raw_v4 = vmalloc(ts->raw_len);
if (!ts->raw_v4)
goto alloc_out;
memset(ts->raw_v0, 0, ts->raw_len);
memset(ts->raw_v1, 0, ts->raw_len);
memset(ts->raw_v2, 0, ts->raw_len);
memset(ts->raw_v3, 0, ts->raw_len);
memset(ts->raw_v4, 0, ts->raw_len);
ts->raw_u8 = kzalloc(ts->raw_len, GFP_KERNEL);
if (!ts->raw_u8)
goto alloc_out;
ts->raw = kzalloc(ts->raw_len, GFP_KERNEL);
if (!ts->raw)
goto alloc_out;
return 0;
alloc_out:
if (!ts->raw_v0)
vfree(ts->raw_v0);
if (!ts->raw_v1)
vfree(ts->raw_v1);
if (!ts->raw_v2)
vfree(ts->raw_v2);
if (!ts->raw_v3)
vfree(ts->raw_v3);
if (!ts->raw_v4)
vfree(ts->raw_v4);
if (!ts->raw_u8)
kfree(ts->raw_u8);
if (!ts->raw)
kfree(ts->raw);
return 0;
}
int stm_ts_rawdata_init(struct stm_ts_data *ts)
{
int alloc_ret = 0, cdev_err = 0;
unsigned int mmapdev_major0;
unsigned int mmapdev_major1;
unsigned int mmapdev_major2;
unsigned int mmapdev_major3;
unsigned int mmapdev_major4;
input_info(true, &ts->client->dev, "%s: num: %d\n", __func__, ts->plat_data->support_rawdata_map_num);
ts->raw_len = PAGE_SIZE;
/* map0 */
mmapdev_cdev0 = cdev_alloc();
alloc_ret = alloc_chrdev_region(&dev0, MINOR_BASE, MINOR_NUM0, DEV_NAME0);
if (alloc_ret != 0) {
input_info(true, &ts->client->dev, "%s: alloc_chrdev_region = %d\n", __func__, alloc_ret);
return -1;
}
mmapdev_major0 = MAJOR(dev0);
dev0 = MKDEV(mmapdev_major0, MINOR_BASE);
cdev_init(mmapdev_cdev0, &mmap0_fops);
mmapdev_cdev0->owner = THIS_MODULE;
cdev_err = cdev_add(mmapdev_cdev0, dev0, MINOR_NUM0);
if (cdev_err != 0) {
input_info(true, &ts->client->dev, "%s: cdev_add = %d\n", __func__, cdev_err);
goto OUT2;
}
/* map1 */
mmapdev_cdev1 = cdev_alloc();
alloc_ret = alloc_chrdev_region(&dev1, MINOR_BASE, MINOR_NUM1, DEV_NAME1);
if (alloc_ret != 0) {
input_info(true, &ts->client->dev, "%s: alloc_chrdev_region = %d\n", __func__, alloc_ret);
return -1;
}
mmapdev_major1 = MAJOR(dev1);
dev1 = MKDEV(mmapdev_major1, MINOR_BASE);
cdev_init(mmapdev_cdev1, &mmap1_fops);
mmapdev_cdev1->owner = THIS_MODULE;
cdev_err = cdev_add(mmapdev_cdev1, dev1, MINOR_NUM1);
if (cdev_err != 0) {
input_info(true, &ts->client->dev, "%s: cdev_add = %d\n", __func__, cdev_err);
goto OUT2;
}
/* map2 */
mmapdev_cdev2 = cdev_alloc();
alloc_ret = alloc_chrdev_region(&dev2, MINOR_BASE, MINOR_NUM2, DEV_NAME2);
if (alloc_ret != 0) {
input_info(true, &ts->client->dev, "%s: alloc_chrdev_region = %d\n", __func__, alloc_ret);
return -1;
}
mmapdev_major2 = MAJOR(dev2);
dev2 = MKDEV(mmapdev_major2, MINOR_BASE);
cdev_init(mmapdev_cdev2, &mmap2_fops);
mmapdev_cdev2->owner = THIS_MODULE;
cdev_err = cdev_add(mmapdev_cdev2, dev2, MINOR_NUM2);
if (cdev_err != 0) {
input_info(true, &ts->client->dev, "%s: cdev_add = %d\n", __func__, cdev_err);
goto OUT2;
}
/* map3 */
mmapdev_cdev3 = cdev_alloc();
alloc_ret = alloc_chrdev_region(&dev3, MINOR_BASE, MINOR_NUM3, DEV_NAME3);
if (alloc_ret != 0) {
input_info(true, &ts->client->dev, "%s: alloc_chrdev_region = %d\n", __func__, alloc_ret);
return -1;
}
mmapdev_major3 = MAJOR(dev3);
dev3 = MKDEV(mmapdev_major3, MINOR_BASE);
cdev_init(mmapdev_cdev3, &mmap3_fops);
mmapdev_cdev3->owner = THIS_MODULE;
cdev_err = cdev_add(mmapdev_cdev3, dev3, MINOR_NUM3);
if (cdev_err != 0) {
input_info(true, &ts->client->dev, "%s: cdev_add = %d\n", __func__, cdev_err);
goto OUT2;
}
/* map4 */
mmapdev_cdev4 = cdev_alloc();
alloc_ret = alloc_chrdev_region(&dev4, MINOR_BASE, MINOR_NUM4, DEV_NAME4);
if (alloc_ret != 0) {
input_info(true, &ts->client->dev, "%s: alloc_chrdev_region = %d\n", __func__, alloc_ret);
return -1;
}
mmapdev_major4 = MAJOR(dev4);
dev4 = MKDEV(mmapdev_major4, MINOR_BASE);
cdev_init(mmapdev_cdev4, &mmap4_fops);
mmapdev_cdev4->owner = THIS_MODULE;
cdev_err = cdev_add(mmapdev_cdev4, dev4, MINOR_NUM4);
if (cdev_err != 0) {
input_info(true, &ts->client->dev, "%s: cdev_add = %d\n", __func__, cdev_err);
goto OUT2;
}
/* class create */
mmapdev_class = class_create(THIS_MODULE, "mmap_device");
if (IS_ERR(mmapdev_class)) {
input_info(true, &ts->client->dev, "%s: class_create\n", __func__);
goto OUT;
}
sysfs_create_group(&ts->sec.fac_dev->kobj, &rawdata_attr_group);
device_create(mmapdev_class, NULL, MKDEV(mmapdev_major0, MINOR_BASE), NULL, DEV_NAME0);
device_create(mmapdev_class, NULL, MKDEV(mmapdev_major1, MINOR_BASE), NULL, DEV_NAME1);
device_create(mmapdev_class, NULL, MKDEV(mmapdev_major2, MINOR_BASE), NULL, DEV_NAME2);
device_create(mmapdev_class, NULL, MKDEV(mmapdev_major3, MINOR_BASE), NULL, DEV_NAME3);
device_create(mmapdev_class, NULL, MKDEV(mmapdev_major4, MINOR_BASE), NULL, DEV_NAME4);
input_info(true, &ts->client->dev, "%s: done", __func__);
tsp_ioctl_init();
return 0;
OUT:
cdev_del(mmapdev_cdev4);
cdev_del(mmapdev_cdev3);
cdev_del(mmapdev_cdev2);
cdev_del(mmapdev_cdev1);
cdev_del(mmapdev_cdev0);
OUT2:
unregister_chrdev_region(dev3, MINOR_NUM4);
unregister_chrdev_region(dev2, MINOR_NUM3);
unregister_chrdev_region(dev2, MINOR_NUM2);
unregister_chrdev_region(dev1, MINOR_NUM1);
unregister_chrdev_region(dev0, MINOR_NUM0);
return -1;
}
void stm_ts_rawdata_buffer_remove(struct stm_ts_data *ts)
{
input_info(true, &ts->client->dev, "%s\n", __func__);
kfree(ts->raw);
kfree(ts->raw_u8);
ts->raw = NULL;
ts->raw_u8 = NULL;
cdev_del(mmapdev_cdev4);
cdev_del(mmapdev_cdev3);
cdev_del(mmapdev_cdev2);
cdev_del(mmapdev_cdev1);
cdev_del(mmapdev_cdev0);
unregister_chrdev_region(dev3, MINOR_NUM4);
unregister_chrdev_region(dev2, MINOR_NUM3);
unregister_chrdev_region(dev2, MINOR_NUM2);
unregister_chrdev_region(dev1, MINOR_NUM1);
unregister_chrdev_region(dev0, MINOR_NUM0);
}
#endif //#ifdef RAWDATA_MMAP