kernel_samsung_a34x-permissive/drivers/sensorhub/utility/shub_file_manager.c
2024-04-28 15:51:13 +02:00

270 lines
6.1 KiB
C

/*
* Copyright (C) 2020, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "../comm/shub_iio.h"
#include "../sensorhub/shub_device.h"
#include "../sensormanager/shub_sensor_type.h"
#include "../sensormanager/shub_sensor_manager.h"
#include "shub_dev_core.h"
#include "shub_utility.h"
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/notifier.h>
#define FILE_MANAGER 0xFB
enum {
FM_READ = 0,
FM_WRITE,
FM_REMOVE,
FM_READY,
};
static BLOCKING_NOTIFIER_HEAD(fm_ready_notifier_list);
struct mutex fm_ready_notifier_mutex;
struct work_struct fm_nofifier_work;
struct mutex fm_mutex;
struct completion fm_done;
bool is_fm_ready;
struct fm_msg_t {
int32_t result;
int32_t rx_buf_size;
char rx_buf[PAGE_SIZE];
int32_t tx_buf_size;
char tx_buf[PAGE_SIZE];
bool wait;
};
struct fm_msg_t fm_msg;
static void fm_notifier_work_func(struct work_struct *work)
{
mutex_lock(&fm_ready_notifier_mutex);
is_fm_ready = true;
shub_infof("");
blocking_notifier_call_chain(&fm_ready_notifier_list, FM_READY, NULL);
mutex_unlock(&fm_ready_notifier_mutex);
}
static void file_manager_ready(void)
{
shub_infof("");
shub_queue_work(&fm_nofifier_work);
}
void unregister_file_manager_ready_callback(struct notifier_block *nb)
{
mutex_lock(&fm_ready_notifier_mutex);
blocking_notifier_chain_unregister(&fm_ready_notifier_list, nb);
mutex_unlock(&fm_ready_notifier_mutex);
}
void register_file_manager_ready_callback(struct notifier_block *nb)
{
mutex_lock(&fm_ready_notifier_mutex);
blocking_notifier_chain_register(&fm_ready_notifier_list, nb);
shub_infof("");
if (is_fm_ready)
nb->notifier_call(nb, FM_READY, NULL);
mutex_unlock(&fm_ready_notifier_mutex);
}
static int _shub_file_rw(char type, bool wait)
{
char send_buf[2];
int ret, result = 0;
send_buf[0] = FILE_MANAGER;
send_buf[1] = type;
fm_msg.wait = wait;
init_completion(&fm_done);
shub_report_sensordata(SENSOR_TYPE_SENSORHUB, get_current_timestamp(), send_buf, sizeof(send_buf));
if (wait) {
ret = wait_for_completion_timeout(&fm_done, msecs_to_jiffies(1000));
if (!ret) {
shub_errf("time out");
result = -EIO;
} else {
result = fm_msg.result;
}
} else {
ret = wait_for_completion_timeout(&fm_done, msecs_to_jiffies(500));
if (!ret)
shub_errf("write_no_wait time out");
}
if (!ret) {
memset(fm_msg.tx_buf, 0, fm_msg.tx_buf_size);
fm_msg.tx_buf_size = 0;
}
return result;
}
static int _shub_file_write(char *path, char *buf, int buf_len, long long pos, bool wait)
{
int ret;
if (!is_fm_ready)
return -ENODEV;
mutex_lock(&fm_mutex);
fm_msg.tx_buf_size =
snprintf(fm_msg.tx_buf, sizeof(fm_msg.tx_buf), "%s,%d,%lld,%d,", path, buf_len, pos, wait);
memcpy(&fm_msg.tx_buf[fm_msg.tx_buf_size], buf, buf_len);
fm_msg.tx_buf_size += buf_len;
ret = _shub_file_rw(FM_WRITE, wait);
mutex_unlock(&fm_mutex);
return ret;
}
int shub_file_write_no_wait(char *path, char *buf, int buf_len, long long pos)
{
int ret = 0;
ret = _shub_file_write(path, buf, buf_len, pos, false);
return (ret < 0) ? ret : buf_len;
}
int shub_file_write(char *path, char *buf, int buf_len, long long pos)
{
return _shub_file_write(path, buf, buf_len, pos, true);
}
int shub_file_read(char *path, char *buf, int buf_len, long long pos)
{
int ret;
if (!is_fm_ready)
return -ENODEV;
mutex_lock(&fm_mutex);
fm_msg.tx_buf_size = snprintf(fm_msg.tx_buf, sizeof(fm_msg.tx_buf), "%s,%d,%lld", path, buf_len, pos);
ret = _shub_file_rw(FM_READ, true);
if (ret > 0)
memcpy(buf, fm_msg.rx_buf, buf_len);
mutex_unlock(&fm_mutex);
return ret;
}
int shub_file_remove(char *path)
{
int ret;
if (!is_fm_ready)
return -ENODEV;
mutex_lock(&fm_mutex);
fm_msg.tx_buf_size = snprintf(fm_msg.tx_buf, sizeof(fm_msg.tx_buf), "%s", path);
ret = _shub_file_rw(FM_REMOVE, true);
mutex_unlock(&fm_mutex);
return ret;
}
ssize_t shub_file_show(struct device *dev, struct device_attribute *attr, char *buf)
{
if (fm_msg.tx_buf_size == 0)
return -EINVAL;
memcpy(buf, fm_msg.tx_buf, fm_msg.tx_buf_size);
if (!fm_msg.wait)
complete(&fm_done);
return fm_msg.tx_buf_size;
}
ssize_t shub_file_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
if (size < 1) {
shub_errf("error %d", (int)size);
return -EINVAL;
}
if (!is_fm_ready) {
if (buf[0] == FM_READY)
file_manager_ready();
} else {
if (size < 5) {
shub_errf("error %d", (int)size);
return -EINVAL;
}
memset(fm_msg.tx_buf, 0, fm_msg.tx_buf_size);
fm_msg.tx_buf_size = 0;
memset(fm_msg.rx_buf, 0, PAGE_SIZE);
memcpy(&fm_msg.result, &buf[1], sizeof(int32_t));
if (buf[0] == FM_READ && fm_msg.result > 0) {
fm_msg.rx_buf_size = fm_msg.result;
memcpy(fm_msg.rx_buf, &buf[5], fm_msg.rx_buf_size);
}
complete(&fm_done);
}
return size;
}
static DEVICE_ATTR(shub_file, 0660, shub_file_show, shub_file_store);
static struct device_attribute *shub_attrs[] = {
&dev_attr_shub_file,
NULL,
};
int init_file_manager(void)
{
int ret;
struct shub_data_t *data = get_shub_data();
mutex_init(&fm_mutex);
mutex_init(&fm_ready_notifier_mutex);
INIT_WORK(&fm_nofifier_work, fm_notifier_work_func);
ret = sensor_device_create(&data->sysfs_dev, data, "ssp_sensor");
if (ret < 0) {
shub_errf("fail to creat ssp_sensor device");
return ret;
}
ret = add_sensor_device_attr(data->sysfs_dev, shub_attrs);
if (ret < 0)
shub_errf("fail to add shub device attr");
return ret;
}
void remove_file_manager(void)
{
struct shub_data_t *data = get_shub_data();
cancel_work_sync(&fm_nofifier_work);
mutex_destroy(&fm_ready_notifier_mutex);
mutex_destroy(&fm_mutex);
remove_sensor_device_attr(data->sysfs_dev, shub_attrs);
sensor_device_destroy(data->sysfs_dev);
}