/* * 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 #include #include #include #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); }