| /* |
| This file is provided under a dual BSD/GPLv2 license. When using or |
| redistributing this file, you may do so under either license. |
| |
| GPL LICENSE SUMMARY |
| Copyright(c) 2014 Intel Corporation. |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of version 2 of the GNU General Public License as |
| published by the Free Software Foundation. |
| |
| 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. |
| |
| Contact Information: |
| qat-linux@intel.com |
| |
| BSD LICENSE |
| Copyright(c) 2014 Intel Corporation. |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| * Neither the name of Intel Corporation nor the names of its |
| contributors may be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include <linux/mutex.h> |
| #include <linux/slab.h> |
| #include <linux/list.h> |
| #include <linux/seq_file.h> |
| #include "adf_accel_devices.h" |
| #include "adf_cfg.h" |
| #include "adf_common_drv.h" |
| |
| static DEFINE_MUTEX(qat_cfg_read_lock); |
| |
| static void *qat_dev_cfg_start(struct seq_file *sfile, loff_t *pos) |
| { |
| struct adf_cfg_device_data *dev_cfg = sfile->private; |
| |
| mutex_lock(&qat_cfg_read_lock); |
| return seq_list_start(&dev_cfg->sec_list, *pos); |
| } |
| |
| static int qat_dev_cfg_show(struct seq_file *sfile, void *v) |
| { |
| struct list_head *list; |
| struct adf_cfg_section *sec = |
| list_entry(v, struct adf_cfg_section, list); |
| |
| seq_printf(sfile, "[%s]\n", sec->name); |
| list_for_each(list, &sec->param_head) { |
| struct adf_cfg_key_val *ptr = |
| list_entry(list, struct adf_cfg_key_val, list); |
| seq_printf(sfile, "%s = %s\n", ptr->key, ptr->val); |
| } |
| return 0; |
| } |
| |
| static void *qat_dev_cfg_next(struct seq_file *sfile, void *v, loff_t *pos) |
| { |
| struct adf_cfg_device_data *dev_cfg = sfile->private; |
| |
| return seq_list_next(v, &dev_cfg->sec_list, pos); |
| } |
| |
| static void qat_dev_cfg_stop(struct seq_file *sfile, void *v) |
| { |
| mutex_unlock(&qat_cfg_read_lock); |
| } |
| |
| static const struct seq_operations qat_dev_cfg_sops = { |
| .start = qat_dev_cfg_start, |
| .next = qat_dev_cfg_next, |
| .stop = qat_dev_cfg_stop, |
| .show = qat_dev_cfg_show |
| }; |
| |
| static int qat_dev_cfg_open(struct inode *inode, struct file *file) |
| { |
| int ret = seq_open(file, &qat_dev_cfg_sops); |
| |
| if (!ret) { |
| struct seq_file *seq_f = file->private_data; |
| |
| seq_f->private = inode->i_private; |
| } |
| return ret; |
| } |
| |
| static const struct file_operations qat_dev_cfg_fops = { |
| .open = qat_dev_cfg_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = seq_release |
| }; |
| |
| /** |
| * adf_cfg_dev_add() - Create an acceleration device configuration table. |
| * @accel_dev: Pointer to acceleration device. |
| * |
| * Function creates a configuration table for the given acceleration device. |
| * The table stores device specific config values. |
| * To be used by QAT device specific drivers. |
| * |
| * Return: 0 on success, error code othewise. |
| */ |
| int adf_cfg_dev_add(struct adf_accel_dev *accel_dev) |
| { |
| struct adf_cfg_device_data *dev_cfg_data; |
| |
| dev_cfg_data = kzalloc(sizeof(*dev_cfg_data), GFP_KERNEL); |
| if (!dev_cfg_data) |
| return -ENOMEM; |
| INIT_LIST_HEAD(&dev_cfg_data->sec_list); |
| init_rwsem(&dev_cfg_data->lock); |
| accel_dev->cfg = dev_cfg_data; |
| |
| /* accel_dev->debugfs_dir should always be non-NULL here */ |
| dev_cfg_data->debug = debugfs_create_file("dev_cfg", S_IRUSR, |
| accel_dev->debugfs_dir, |
| dev_cfg_data, |
| &qat_dev_cfg_fops); |
| if (!dev_cfg_data->debug) { |
| dev_err(&GET_DEV(accel_dev), |
| "Failed to create qat cfg debugfs entry.\n"); |
| kfree(dev_cfg_data); |
| accel_dev->cfg = NULL; |
| return -EFAULT; |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(adf_cfg_dev_add); |
| |
| static void adf_cfg_section_del_all(struct list_head *head); |
| |
| void adf_cfg_del_all(struct adf_accel_dev *accel_dev) |
| { |
| struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg; |
| |
| down_write(&dev_cfg_data->lock); |
| adf_cfg_section_del_all(&dev_cfg_data->sec_list); |
| up_write(&dev_cfg_data->lock); |
| clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); |
| } |
| |
| /** |
| * adf_cfg_dev_remove() - Clears acceleration device configuration table. |
| * @accel_dev: Pointer to acceleration device. |
| * |
| * Function removes configuration table from the given acceleration device |
| * and frees all allocated memory. |
| * To be used by QAT device specific drivers. |
| * |
| * Return: void |
| */ |
| void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev) |
| { |
| struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg; |
| |
| down_write(&dev_cfg_data->lock); |
| adf_cfg_section_del_all(&dev_cfg_data->sec_list); |
| up_write(&dev_cfg_data->lock); |
| debugfs_remove(dev_cfg_data->debug); |
| kfree(dev_cfg_data); |
| accel_dev->cfg = NULL; |
| } |
| EXPORT_SYMBOL_GPL(adf_cfg_dev_remove); |
| |
| static void adf_cfg_keyval_add(struct adf_cfg_key_val *new, |
| struct adf_cfg_section *sec) |
| { |
| list_add_tail(&new->list, &sec->param_head); |
| } |
| |
| static void adf_cfg_keyval_del_all(struct list_head *head) |
| { |
| struct list_head *list_ptr, *tmp; |
| |
| list_for_each_prev_safe(list_ptr, tmp, head) { |
| struct adf_cfg_key_val *ptr = |
| list_entry(list_ptr, struct adf_cfg_key_val, list); |
| list_del(list_ptr); |
| kfree(ptr); |
| } |
| } |
| |
| static void adf_cfg_section_del_all(struct list_head *head) |
| { |
| struct adf_cfg_section *ptr; |
| struct list_head *list, *tmp; |
| |
| list_for_each_prev_safe(list, tmp, head) { |
| ptr = list_entry(list, struct adf_cfg_section, list); |
| adf_cfg_keyval_del_all(&ptr->param_head); |
| list_del(list); |
| kfree(ptr); |
| } |
| } |
| |
| static struct adf_cfg_key_val *adf_cfg_key_value_find(struct adf_cfg_section *s, |
| const char *key) |
| { |
| struct list_head *list; |
| |
| list_for_each(list, &s->param_head) { |
| struct adf_cfg_key_val *ptr = |
| list_entry(list, struct adf_cfg_key_val, list); |
| if (!strcmp(ptr->key, key)) |
| return ptr; |
| } |
| return NULL; |
| } |
| |
| static struct adf_cfg_section *adf_cfg_sec_find(struct adf_accel_dev *accel_dev, |
| const char *sec_name) |
| { |
| struct adf_cfg_device_data *cfg = accel_dev->cfg; |
| struct list_head *list; |
| |
| list_for_each(list, &cfg->sec_list) { |
| struct adf_cfg_section *ptr = |
| list_entry(list, struct adf_cfg_section, list); |
| if (!strcmp(ptr->name, sec_name)) |
| return ptr; |
| } |
| return NULL; |
| } |
| |
| static int adf_cfg_key_val_get(struct adf_accel_dev *accel_dev, |
| const char *sec_name, |
| const char *key_name, |
| char *val) |
| { |
| struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, sec_name); |
| struct adf_cfg_key_val *keyval = NULL; |
| |
| if (sec) |
| keyval = adf_cfg_key_value_find(sec, key_name); |
| if (keyval) { |
| memcpy(val, keyval->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| return 0; |
| } |
| return -1; |
| } |
| |
| /** |
| * adf_cfg_add_key_value_param() - Add key-value config entry to config table. |
| * @accel_dev: Pointer to acceleration device. |
| * @section_name: Name of the section where the param will be added |
| * @key: The key string |
| * @val: Value pain for the given @key |
| * @type: Type - string, int or address |
| * |
| * Function adds configuration key - value entry in the appropriate section |
| * in the given acceleration device |
| * To be used by QAT device specific drivers. |
| * |
| * Return: 0 on success, error code othewise. |
| */ |
| int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev, |
| const char *section_name, |
| const char *key, const void *val, |
| enum adf_cfg_val_type type) |
| { |
| struct adf_cfg_device_data *cfg = accel_dev->cfg; |
| struct adf_cfg_key_val *key_val; |
| struct adf_cfg_section *section = adf_cfg_sec_find(accel_dev, |
| section_name); |
| if (!section) |
| return -EFAULT; |
| |
| key_val = kzalloc(sizeof(*key_val), GFP_KERNEL); |
| if (!key_val) |
| return -ENOMEM; |
| |
| INIT_LIST_HEAD(&key_val->list); |
| strlcpy(key_val->key, key, sizeof(key_val->key)); |
| |
| if (type == ADF_DEC) { |
| snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES, |
| "%ld", (*((long *)val))); |
| } else if (type == ADF_STR) { |
| strlcpy(key_val->val, (char *)val, sizeof(key_val->val)); |
| } else if (type == ADF_HEX) { |
| snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES, |
| "0x%lx", (unsigned long)val); |
| } else { |
| dev_err(&GET_DEV(accel_dev), "Unknown type given.\n"); |
| kfree(key_val); |
| return -1; |
| } |
| key_val->type = type; |
| down_write(&cfg->lock); |
| adf_cfg_keyval_add(key_val, section); |
| up_write(&cfg->lock); |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(adf_cfg_add_key_value_param); |
| |
| /** |
| * adf_cfg_section_add() - Add config section entry to config table. |
| * @accel_dev: Pointer to acceleration device. |
| * @name: Name of the section |
| * |
| * Function adds configuration section where key - value entries |
| * will be stored. |
| * To be used by QAT device specific drivers. |
| * |
| * Return: 0 on success, error code othewise. |
| */ |
| int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name) |
| { |
| struct adf_cfg_device_data *cfg = accel_dev->cfg; |
| struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, name); |
| |
| if (sec) |
| return 0; |
| |
| sec = kzalloc(sizeof(*sec), GFP_KERNEL); |
| if (!sec) |
| return -ENOMEM; |
| |
| strlcpy(sec->name, name, sizeof(sec->name)); |
| INIT_LIST_HEAD(&sec->param_head); |
| down_write(&cfg->lock); |
| list_add_tail(&sec->list, &cfg->sec_list); |
| up_write(&cfg->lock); |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(adf_cfg_section_add); |
| |
| int adf_cfg_get_param_value(struct adf_accel_dev *accel_dev, |
| const char *section, const char *name, |
| char *value) |
| { |
| struct adf_cfg_device_data *cfg = accel_dev->cfg; |
| int ret; |
| |
| down_read(&cfg->lock); |
| ret = adf_cfg_key_val_get(accel_dev, section, name, value); |
| up_read(&cfg->lock); |
| return ret; |
| } |