kernel_samsung_a53x/drivers/net/wireless/scsc/scsc_wlan_mmap.c
2024-06-15 16:02:09 -03:00

174 lines
4.2 KiB
C
Executable file

/****************************************************************************
*
* Copyright (c) 2014 - 2018 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include "debug.h"
#define N_MINORS 1
#define SCSC_WLAN_MAX_SIZE 4*1024*1024
static struct class *scsc_wlan_mmap_class;
static struct cdev scsc_wlan_mmap_dev[N_MINORS];
static dev_t dev_num;
static DEFINE_MUTEX(scsc_wlan_lock);
static void *scsc_wlan_mmap_buf;
static size_t scsc_wlan_mmap_size;
static int scsc_wlan_mmap_open(struct inode *inode, struct file *filp)
{
SLSI_INFO_NODEV("scsc_wlan_mmap_open\n");
return 0;
}
static int scsc_wlan_mmap_release(struct inode *inode, struct file *filp)
{
SLSI_INFO_NODEV("scsc_wlan_mmap_release\n");
return 0;
}
static int scsc_wlan_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
/* Ignore the vma->vm_pgoff , we are forcing mmap to
* start on offset 0*/
unsigned long offset = 0;
unsigned long page, pos;
mutex_lock(&scsc_wlan_lock);
if (size > scsc_wlan_mmap_size)
goto error;
if (offset > scsc_wlan_mmap_size - size)
goto error;
if (scsc_wlan_mmap_buf == NULL) {
SLSI_ERR_NODEV("scsc_wlan_mmap_buf is NULL\n");
goto error;
}
pos = (unsigned long)scsc_wlan_mmap_buf + offset;
SLSI_INFO_NODEV("scsc_wlan_mmap size:%lu offset %ld\n", size, offset);
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
goto error;
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
mutex_unlock(&scsc_wlan_lock);
return 0;
error:
mutex_unlock(&scsc_wlan_lock);
return -EAGAIN;
}
int scsc_wlan_mmap_set_buffer(void *buf, size_t sz)
{
mutex_lock(&scsc_wlan_lock);
if (sz > SCSC_WLAN_MAX_SIZE) {
SLSI_ERR_NODEV("Size %zu exceeds %d\n", sz, SCSC_WLAN_MAX_SIZE);
scsc_wlan_mmap_buf = NULL;
scsc_wlan_mmap_size = 0;
mutex_unlock(&scsc_wlan_lock);
return -EIO;
}
SLSI_INFO_NODEV("scsc_wlan_mmap_set_buffer size:%zu\n", sz);
scsc_wlan_mmap_buf = buf;
scsc_wlan_mmap_size = sz;
mutex_unlock(&scsc_wlan_lock);
return 0;
}
static const struct file_operations scsc_wlan_mmap_fops = {
.owner = THIS_MODULE,
.open = scsc_wlan_mmap_open,
.mmap = scsc_wlan_mmap,
.release = scsc_wlan_mmap_release,
};
int scsc_wlan_mmap_create(void)
{
struct device *dev;
int i;
int ret;
dev_t curr_dev;
/* Request the kernel for N_MINOR devices */
ret = alloc_chrdev_region(&dev_num, 0, N_MINORS, "scsc_wlan_mmap");
if (ret) {
SLSI_ERR_NODEV("alloc_chrdev_region failed");
goto error;
}
/* Create a class : appears at /sys/class */
scsc_wlan_mmap_class = class_create(THIS_MODULE, "scsc_wlan_mmap_class");
if (IS_ERR(scsc_wlan_mmap_class)) {
ret = PTR_ERR(scsc_wlan_mmap_class);
goto error_class;
}
/* Initialize and create each of the device(cdev) */
for (i = 0; i < N_MINORS; i++) {
curr_dev = MKDEV(MAJOR(dev_num), i);
/* Associate the cdev with a set of file_operations */
cdev_init(&scsc_wlan_mmap_dev[i], &scsc_wlan_mmap_fops);
ret = cdev_add(&scsc_wlan_mmap_dev[i], curr_dev, 1);
if (ret) {
SLSI_ERR_NODEV("cdev_add failed");
continue;
}
scsc_wlan_mmap_dev[i].owner = THIS_MODULE;
/* Build up the current device number. To be used further */
dev = device_create(scsc_wlan_mmap_class, NULL, curr_dev, NULL, "scsc_wlan_mmap_%d", i);
if (IS_ERR(dev)) {
SLSI_ERR_NODEV("device_create failed");
ret = PTR_ERR(dev);
cdev_del(&scsc_wlan_mmap_dev[i]);
continue;
}
}
return 0;
error_class:
unregister_chrdev_region(dev_num, N_MINORS);
error:
return 0;
}
int scsc_wlan_mmap_destroy(void)
{
int i;
int major = MAJOR(dev_num);
dev_t curr_dev;
for (i = 0; i < N_MINORS; i++) {
curr_dev = MKDEV(major, i);
cdev_del(&scsc_wlan_mmap_dev[i]);
device_destroy(scsc_wlan_mmap_class, dev_num);
}
class_destroy(scsc_wlan_mmap_class);
unregister_chrdev_region(dev_num, N_MINORS);
return 0;
}