/* * * remote_config_server.c * Description: * remote_config over MRPC server driver * * * GPL LICENSE SUMMARY * * Copyright(c) 2016-2018 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. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Corporation * 2200 Mission College Blvd. * Santa Clara, CA 97052 */ #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d " fmt "\n", __func__, __LINE__ /************************************************/ /** Includes */ /************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define DBG(fmt, ...) pr_err(fmt, ##__VA_ARGS__) #else #define DBG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #endif /* driver private database */ struct remote_config_private { struct platform_device *pdev; /* platform device */ struct mrpc_server *mrpc; /* mrpc server handle */ struct remote_config_funcs *callbacks; }; struct remote_config_args { avalanche_pp_local_dev_addr_ioctl_params_t usr_params; }; #define REMOTE_CONFIG_PROC(proc, func) \ MRPC_PROCINFO_ENTRY(proc, func, sizeof(struct remote_config_args), sizeof(unsigned int)) enum { REMOTE_CONFIG_SET_DEV_ADDR = 0, }; static struct remote_config_private *this; static int mrpc_remote_config_proc(void *arg, ssize_t arglen, void *rep, ssize_t replen) { struct remote_config_args *p_arg = (struct remote_config_args *)arg; struct remote_config_private *priv = this; unsigned int *reply = (unsigned int*)rep; int ret = 0; p_arg->usr_params.addr_type = be32_to_cpu(p_arg->usr_params.addr_type); p_arg->usr_params.op_type = be32_to_cpu(p_arg->usr_params.op_type); switch (p_arg->usr_params.addr_type) { /* MAC Address - no need to change endianity */ case MTA_MAC_ADDR: case GW_MAC_ADDR: case LAN0_MAC_ADDR: case RND_MAC_ADDR: case WAN_MAC_ADDR: break; /* IPv4 - Change endianity */ case MTA_RTP_IPV4_ADDR: case MTA_MNG_IPV4_ADDR: case MULTI_DROP_IPV4_ADDR: { p_arg->usr_params.u.ipv4 = be32_to_cpu(p_arg->usr_params.u.ipv4); break; } /* IPv6 - Change endianity */ case MTA_RTP_IPV6_ADDR: case MTA_MNG_IPV6_ADDR: case MULTI_DROP_IPV6_ADDR: { p_arg->usr_params.u.ipv6[0] = be32_to_cpu(p_arg->usr_params.u.ipv6[0]); p_arg->usr_params.u.ipv6[1] = be32_to_cpu(p_arg->usr_params.u.ipv6[1]); p_arg->usr_params.u.ipv6[2] = be32_to_cpu(p_arg->usr_params.u.ipv6[2]); p_arg->usr_params.u.ipv6[3] = be32_to_cpu(p_arg->usr_params.u.ipv6[3]); break; } default: { pr_err("ERROR: mrpc remote_config not initialized"); return -1; } } *reply = (unsigned int)priv->callbacks->avalanche_pp_local_dev_addr(&p_arg->usr_params); return ret; } static struct mrpc_procinfo remote_config_server_procs[] = { REMOTE_CONFIG_PROC(REMOTE_CONFIG_SET_DEV_ADDR, mrpc_remote_config_proc), }; /* sysfs for future use */ static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct remote_config_private *priv = dev_get_drvdata(dev); if (!priv) return -EINVAL; return scnprintf(buf, PAGE_SIZE, "status ok"); } static DEVICE_ATTR(status, S_IRUGO, status_show, NULL); static struct attribute *remote_config_attrs[] = { &dev_attr_status.attr, NULL }; static struct attribute_group remote_config_attrs_group = { .attrs = remote_config_attrs, }; /** * mrpc_remote_config_server_unregister * * unregister remote_config callbacks * * @return 0 for success, error code otherwise */ void mrpc_remote_config_server_unregister(void) { BUG_ON(!this); BUG_ON(!this->callbacks); kfree(this->callbacks); this->callbacks = NULL; pr_info("callback functions unregistered\n"); } EXPORT_SYMBOL(mrpc_remote_config_server_unregister); /** * mrpc_remote_config_server_register * * register remote_config callbacks * * @param remote_config_funcs callback functions * * @return 0 for success, error code otherwise */ int mrpc_remote_config_server_register(struct remote_config_funcs *callbacks) { BUG_ON(!this); BUG_ON(!callbacks); BUG_ON(this->callbacks); this->callbacks = kzalloc(sizeof(*callbacks), GFP_KERNEL); if (!this->callbacks) { pr_err("Failed to allocate memory"); return -ENOMEM; } memcpy(this->callbacks, callbacks, sizeof(*callbacks)); pr_info("callback functions registered"); return 0; } EXPORT_SYMBOL(mrpc_remote_config_server_register); /** * remote_config_probe * * @param pdev platform device * * @return 0 for success, error code otherwise */ static int remote_config_probe(struct platform_device *pdev) { struct remote_config_private *priv; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { pr_err("memory allocation failed"); return -ENOMEM; } dev_set_drvdata(&pdev->dev, priv); priv->pdev = pdev; ret = sysfs_create_group(&priv->pdev->dev.kobj, &remote_config_attrs_group); if (ret) { pr_err("sysfs_create_group failed (ret=%d)", ret); return ret; } priv->mrpc = mrpc_server_register(MRPC_RESERVED_ID_REMOTE_CONFIG, "Remote Config", remote_config_server_procs, ARRAY_SIZE(remote_config_server_procs)); if (!priv->mrpc) { pr_err("failed to register remote_config"); ret = -ENODEV; goto out_remove_group; } this = priv; return 0; out_remove_group: sysfs_remove_group(&priv->pdev->dev.kobj, &remote_config_attrs_group); return ret; } /** * remote_config_remove * * This function is called when the remote_config driver is * removed. * * @param pdev platform device * * @return 0 for success, error code otherwise */ static int remote_config_remove(struct platform_device *pdev) { struct remote_config_private *priv = platform_get_drvdata(pdev); if (priv->callbacks) kfree(priv->callbacks); mrpc_server_unregister(priv->mrpc); sysfs_remove_group(&priv->pdev->dev.kobj, &remote_config_attrs_group); dev_set_drvdata(&pdev->dev, NULL); this = NULL; return 0; } static struct platform_driver remote_config_driver = { .driver = { .name = "remote_config_mrpc_server", }, .probe = remote_config_probe, .remove = remote_config_remove, }; static struct platform_device *remote_config_device; static int remote_config_server_init(void) { int ret; printk("remote_config_server_init: remote_config_args_size=%d\n", sizeof(struct remote_config_args)); ret = platform_driver_register(&remote_config_driver); if (ret < 0) { pr_err("Failed to register remote_config platform driver: %d\n", ret); return ret; } pr_debug("remote config server platform driver is registered"); remote_config_device = platform_device_register_simple("remote_config_mrpc_server", -1, NULL, 0); if (IS_ERR(remote_config_device)) { pr_err("Failed to register remote_config platform device\n"); platform_driver_unregister(&remote_config_driver); return PTR_ERR(remote_config_device); } dev_info(&remote_config_device->dev, "platform device is registered\n"); return 0; } static void __exit remote_config_server_exit(void) { platform_device_unregister(remote_config_device); platform_driver_unregister(&remote_config_driver); } /******************************************************/ /** Module Declarations **/ /******************************************************/ static_notifier_module_init(mrpc_ready, remote_config_server_init); module_exit(remote_config_server_exit); MODULE_AUTHOR("Intel Corporation"); MODULE_AUTHOR("Ofir Bitton ofir1.bitton@intel.com"); MODULE_DESCRIPTION("remote_config mrpc server"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0");