| // SPDX-License-Identifier: GPL-2.0-only |
| /* Copyright(c) 2023 Intel Corporation */ |
| |
| #include <linux/array_size.h> |
| #include <linux/bitops.h> |
| #include <linux/export.h> |
| #include <linux/pci.h> |
| #include <linux/string.h> |
| #include "adf_cfg.h" |
| #include "adf_cfg_services.h" |
| #include "adf_cfg_strings.h" |
| |
| static const char *const adf_cfg_services[] = { |
| [SVC_ASYM] = ADF_CFG_ASYM, |
| [SVC_SYM] = ADF_CFG_SYM, |
| [SVC_DC] = ADF_CFG_DC, |
| [SVC_DCC] = ADF_CFG_DCC, |
| }; |
| |
| /* |
| * Ensure that the size of the array matches the number of services, |
| * SVC_BASE_COUNT, that is used to size the bitmap. |
| */ |
| static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_BASE_COUNT); |
| |
| /* |
| * Ensure that the maximum number of concurrent services that can be |
| * enabled on a device is less than or equal to the number of total |
| * supported services. |
| */ |
| static_assert(ARRAY_SIZE(adf_cfg_services) >= MAX_NUM_CONCURR_SVC); |
| |
| /* |
| * Ensure that the number of services fit a single unsigned long, as each |
| * service is represented by a bit in the mask. |
| */ |
| static_assert(BITS_PER_LONG >= SVC_BASE_COUNT); |
| |
| /* |
| * Ensure that size of the concatenation of all service strings is smaller |
| * than the size of the buffer that will contain them. |
| */ |
| static_assert(sizeof(ADF_CFG_SYM ADF_SERVICES_DELIMITER |
| ADF_CFG_ASYM ADF_SERVICES_DELIMITER |
| ADF_CFG_DC ADF_SERVICES_DELIMITER |
| ADF_CFG_DCC) < ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| |
| static int adf_service_string_to_mask(struct adf_accel_dev *accel_dev, const char *buf, |
| size_t len, unsigned long *out_mask) |
| { |
| struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); |
| char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { }; |
| unsigned long mask = 0; |
| char *substr, *token; |
| int id, num_svc = 0; |
| |
| if (len > ADF_CFG_MAX_VAL_LEN_IN_BYTES - 1) |
| return -EINVAL; |
| |
| strscpy(services, buf, ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| substr = services; |
| |
| while ((token = strsep(&substr, ADF_SERVICES_DELIMITER))) { |
| id = sysfs_match_string(adf_cfg_services, token); |
| if (id < 0) |
| return id; |
| |
| if (test_and_set_bit(id, &mask)) |
| return -EINVAL; |
| |
| if (num_svc++ == MAX_NUM_CONCURR_SVC) |
| return -EINVAL; |
| } |
| |
| if (hw_data->services_supported && !hw_data->services_supported(mask)) |
| return -EINVAL; |
| |
| *out_mask = mask; |
| |
| return 0; |
| } |
| |
| static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len) |
| { |
| int offset = 0; |
| int bit; |
| |
| if (len < ADF_CFG_MAX_VAL_LEN_IN_BYTES) |
| return -ENOSPC; |
| |
| for_each_set_bit(bit, &mask, SVC_BASE_COUNT) { |
| if (offset) |
| offset += scnprintf(buf + offset, len - offset, |
| ADF_SERVICES_DELIMITER); |
| |
| offset += scnprintf(buf + offset, len - offset, "%s", |
| adf_cfg_services[bit]); |
| } |
| |
| return 0; |
| } |
| |
| int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in, |
| size_t in_len, char *out, size_t out_len) |
| { |
| unsigned long mask; |
| int ret; |
| |
| ret = adf_service_string_to_mask(accel_dev, in, in_len, &mask); |
| if (ret) |
| return ret; |
| |
| if (!mask) |
| return -EINVAL; |
| |
| return adf_service_mask_to_string(mask, out, out_len); |
| } |
| |
| static int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask) |
| { |
| char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { }; |
| size_t len; |
| int ret; |
| |
| ret = adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC, |
| ADF_SERVICES_ENABLED, services); |
| if (ret) { |
| dev_err(&GET_DEV(accel_dev), "%s param not found\n", |
| ADF_SERVICES_ENABLED); |
| return ret; |
| } |
| |
| len = strnlen(services, ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| ret = adf_service_string_to_mask(accel_dev, services, len, mask); |
| if (ret) |
| dev_err(&GET_DEV(accel_dev), "Invalid value of %s param: %s\n", |
| ADF_SERVICES_ENABLED, services); |
| |
| return ret; |
| } |
| |
| int adf_get_service_enabled(struct adf_accel_dev *accel_dev) |
| { |
| unsigned long mask; |
| int ret; |
| |
| ret = adf_get_service_mask(accel_dev, &mask); |
| if (ret) |
| return ret; |
| |
| if (test_bit(SVC_SYM, &mask) && test_bit(SVC_ASYM, &mask)) |
| return SVC_SYM_ASYM; |
| |
| if (test_bit(SVC_SYM, &mask) && test_bit(SVC_DC, &mask)) |
| return SVC_SYM_DC; |
| |
| if (test_bit(SVC_ASYM, &mask) && test_bit(SVC_DC, &mask)) |
| return SVC_ASYM_DC; |
| |
| if (test_bit(SVC_SYM, &mask)) |
| return SVC_SYM; |
| |
| if (test_bit(SVC_ASYM, &mask)) |
| return SVC_ASYM; |
| |
| if (test_bit(SVC_DC, &mask)) |
| return SVC_DC; |
| |
| if (test_bit(SVC_DCC, &mask)) |
| return SVC_DCC; |
| |
| return -EINVAL; |
| } |
| EXPORT_SYMBOL_GPL(adf_get_service_enabled); |