/* * Copyright (c) 2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Samsung TN debugging code * * 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 #include #include #include #include #include #include #define PARAM_RD 0 #define PARAM_WR 1 #define SEC_PARAM_NAME "/dev/block/param" #define STR_LENGTH 1024 struct sec_param_data_s { struct work_struct sec_param_work; unsigned long offset; char val; }; struct sec_param_data_s_str { struct work_struct sec_param_work_str; struct completion work; unsigned long offset; char str[STR_LENGTH]; int direction; }; struct sec_param_data_s_extra { struct work_struct sec_param_work; struct completion work; unsigned long offset; void *extra; size_t size; int direction; }; static struct sec_param_data_s sec_param_data; static struct sec_param_data_s_str sec_param_data_str; static struct sec_param_data_s_extra sec_param_data_extra; static DEFINE_MUTEX(sec_param_mutex); static DEFINE_MUTEX(sec_param_mutex_str); static DEFINE_MUTEX(sec_param_mutex_extra); static void sec_param_update(struct work_struct *work) { int ret = -1; struct file *fp; struct sec_param_data_s *param_data = container_of(work, struct sec_param_data_s, sec_param_work); fp = filp_open(SEC_PARAM_NAME, O_WRONLY | O_SYNC, 0); if (IS_ERR(fp)) { pr_err("%s: filp_open error %ld\n", __func__, PTR_ERR(fp)); return; } pr_info("%s: set param %c at %lu\n", __func__, param_data->val, param_data->offset); ret = fp->f_op->llseek(fp, param_data->offset, SEEK_SET); if (ret < 0) { pr_err("%s: llseek error %d!\n", __func__, ret); goto close_fp_out; } ret = vfs_write(fp, ¶m_data->val, 1, &fp->f_pos); if (ret < 0) pr_err("%s: write error! %d\n", __func__, ret); close_fp_out: filp_close(fp, NULL); pr_info("%s: exit %d\n", __func__, ret); } static void sec_param_update_str(struct work_struct *work) { int ret = -1; struct file *fp; mm_segment_t fs; struct sec_param_data_s_str *param_data_str = container_of(work, struct sec_param_data_s_str, sec_param_work_str); int flag = (param_data_str->direction == PARAM_WR) ? (O_RDWR | O_SYNC) : O_RDONLY; fp = filp_open(SEC_PARAM_NAME, flag, 0); if (IS_ERR(fp)) { pr_err("%s: filp_open error %ld\n", __func__, PTR_ERR(fp)); complete(¶m_data_str->work); return; } fs = get_fs(); set_fs(get_ds()); ret = vfs_llseek(fp, param_data_str->offset, SEEK_SET); if (ret < 0) { pr_err("%s: llseek error %d!\n", __func__, ret); goto close_fp_out; } if (param_data_str->direction == PARAM_WR) ret = vfs_write(fp, (const char __user *)param_data_str->str, sizeof(param_data_str->str), &fp->f_pos); else if (param_data_str->direction == PARAM_RD) ret = vfs_read(fp, (char __user *)param_data_str->str, sizeof(param_data_str->str), &fp->f_pos); if (ret < 0) { pr_err("%s: %s error! %d\n", __func__, param_data_str->direction ? "write" : "read", ret); goto close_fp_out; } close_fp_out: set_fs(fs); filp_close(fp, NULL); complete(¶m_data_str->work); pr_info("%s: exit %d\n", __func__, ret); } static void sec_param_update_extra(struct work_struct *work) { int ret = -1; struct file *fp; mm_segment_t fs; struct sec_param_data_s_extra *param_data_extra = container_of(work, struct sec_param_data_s_extra, sec_param_work); int flag = (param_data_extra->direction == PARAM_WR) ? (O_RDWR | O_SYNC) : O_RDONLY; fp = filp_open(SEC_PARAM_NAME, flag, 0); if (IS_ERR(fp)) { pr_err("%s: filp_open error %ld\n", __func__, PTR_ERR(fp)); complete(¶m_data_extra->work); return; } fs = get_fs(); set_fs(get_ds()); ret = vfs_llseek(fp, param_data_extra->offset, SEEK_SET); if (ret < 0) { pr_err("%s: llseek error %d!\n", __func__, ret); goto close_fp_out; } if (param_data_extra->direction == PARAM_WR) ret = vfs_write(fp, (const char __user *)param_data_extra->extra, param_data_extra->size, &fp->f_pos); else if (param_data_extra->direction == PARAM_RD) ret = vfs_read(fp, (char __user *)param_data_extra->extra, param_data_extra->size, &fp->f_pos); if (ret < 0) { pr_err("%s: %s error! %d\n", __func__, param_data_extra->direction ? "write" : "read", ret); goto close_fp_out; } close_fp_out: set_fs(fs); filp_close(fp, NULL); complete(¶m_data_extra->work); pr_info("%s: exit %d\n", __func__, ret); } int sec_set_param(unsigned long offset, char val) { int ret = -1; mutex_lock(&sec_param_mutex); printk("%s offset %lu\n", __func__, offset); switch (offset) { case CM_OFFSET ... CM_OFFSET_LIMIT: break; default: if (offset != FMM_LOCK_OFFSET) goto unlock_out; break; } switch (val) { case PARAM_OFF: case PARAM_ON: goto set_param; default: goto unlock_out; } set_param: sec_param_data.offset = offset; sec_param_data.val = val; schedule_work(&sec_param_data.sec_param_work); /* how to determine to return success or fail ? */ ret = 0; unlock_out: mutex_unlock(&sec_param_mutex); return ret; } int sec_set_param_str(unsigned long offset, const char *str, int size) { int ret = 0; mutex_lock(&sec_param_mutex_str); if (!strcmp(str, "")) { mutex_unlock(&sec_param_mutex_str); return -1; } memset(sec_param_data_str.str, 0, sizeof(sec_param_data_str.str)); sec_param_data_str.offset = offset; sec_param_data_str.direction = PARAM_WR; strncpy(sec_param_data_str.str, str, size); schedule_work(&sec_param_data_str.sec_param_work_str); wait_for_completion_timeout(&sec_param_data_str.work, msecs_to_jiffies(HZ)); mutex_unlock(&sec_param_mutex_str); return ret; } int sec_get_param_str(unsigned long offset, char *str) { mutex_lock(&sec_param_mutex_str); memset(sec_param_data_str.str, 0, sizeof(sec_param_data_str.str)); sec_param_data_str.offset = offset; sec_param_data_str.direction = PARAM_RD; schedule_work(&sec_param_data_str.sec_param_work_str); wait_for_completion_timeout(&sec_param_data_str.work, msecs_to_jiffies(HZ)); snprintf(str, sizeof(sec_param_data_str.str), "%s", sec_param_data_str.str); mutex_unlock(&sec_param_mutex_str); return 0; } int sec_set_param_extra(unsigned long offset, void *extra, size_t size) { int ret = 0; mutex_lock(&sec_param_mutex_extra); if (!extra || !size) { mutex_unlock(&sec_param_mutex_extra); return -1; } sec_param_data_extra.offset = offset; sec_param_data_extra.direction = PARAM_WR; sec_param_data_extra.extra = (void *)extra; sec_param_data_extra.size = size; schedule_work(&sec_param_data_extra.sec_param_work); wait_for_completion_timeout(&sec_param_data_extra.work, msecs_to_jiffies(HZ)); mutex_unlock(&sec_param_mutex_extra); return ret; } static int __init sec_param_work_init(void) { pr_info("%s: start\n", __func__); sec_param_data.offset = 0; sec_param_data.val = '0'; sec_param_data_str.offset = 0; memset(sec_param_data_str.str, 0, sizeof(sec_param_data_str.str)); sec_param_data_str.direction = 0; INIT_WORK(&sec_param_data.sec_param_work, sec_param_update); init_completion(&sec_param_data_str.work); INIT_WORK(&sec_param_data_str.sec_param_work_str, sec_param_update_str); init_completion(&sec_param_data_extra.work); INIT_WORK(&sec_param_data_extra.sec_param_work, sec_param_update_extra); return 0; } static void __exit sec_param_work_exit(void) { cancel_work_sync(&sec_param_data.sec_param_work); pr_info("%s: exit\n", __func__); } module_init(sec_param_work_init); module_exit(sec_param_work_exit);