/* * * handshake_server.c * Description: * handshake over MRPC server driver * * * GPL LICENSE SUMMARY * * Copyright(c) 2016 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 #ifdef DEBUG #define DBG(fmt, ...) pr_err(fmt, ##__VA_ARGS__) #else #define DBG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #endif #define HANDSHAKE_SERVER_NAME "handshake_server" /* driver private database */ struct handshake_private { struct platform_device *pdev; /* platform device */ struct mrpc_server *mrpc; /* mrpc server handle */ }; enum{ BOOT_DONE = 0, BOOT_NOT_DONE, }; #define handshake_PROC(proc, func) \ MRPC_PROCINFO_ENTRY(proc, func, 0, sizeof(int)) enum { HANDSHAKE_BOOT_STATUS = 0, }; /*Set all enabled services to be synced*/ static volatile unsigned int sync_status = SYNC_POINTS; EXPORT_SYMBOL(boot_status_report); /** * boot_status_report * Function to allow different client to report sync point * reached * * @param client_id specific client ID reporting boot status * * @return 0 for success, error code otherwise */ int boot_status_report(int client_id) { pr_info("Client ID = %d reported sync ready. sync_status=0x%x > 0x%x\n", client_id, sync_status, sync_status & ~(1 << client_id)); if (client_id >= HANDSHAKE_ID_LAST) return -1; sync_status &= ~(1 << client_id); return 0; } /** * mrpc_handshake_boot_status * Function to report boot status, will only announce boot is * done when all clients reported sync point reached * * @param rep - pointer to hold boot status * * @return 0 always */ static int mrpc_handshake_boot_status(void *arg, ssize_t arglen, void *rep, ssize_t replen) { pr_info("Call handshake boot status\n"); if (sync_status) *(int *)rep = htonl(BOOT_NOT_DONE); else *(int *)rep = htonl(BOOT_DONE); return 0; } static struct mrpc_procinfo handshake_server_procs[] = { handshake_PROC(HANDSHAKE_BOOT_STATUS, mrpc_handshake_boot_status) }; /* sysfs for future use */ static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct handshake_private *priv = dev_get_drvdata(dev); if (!priv) return -EINVAL; return scnprintf(buf, PAGE_SIZE, "current boot value 0x%x", sync_status); } static DEVICE_ATTR(status, S_IRUGO, status_show, NULL); static struct attribute *handshake_attrs[] = { &dev_attr_status.attr, NULL }; static struct attribute_group handshake_attrs_group = { .attrs = handshake_attrs, }; /** * handshake_probe * * @param pdev platform device * * @return 0 for success, error code otherwise */ static int handshake_probe(struct platform_device *pdev) { struct handshake_private *priv; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { pr_err("memory allocation failed\n"); return -ENOMEM; } dev_set_drvdata(&pdev->dev, priv); priv->pdev = pdev; ret = sysfs_create_group(&priv->pdev->dev.kobj, &handshake_attrs_group); if (ret) { pr_err("sysfs_create_group failed (ret=%d)\n", ret); return ret; } priv->mrpc = mrpc_server_register(MRPC_RESERVED_ID_HANDSHAKE, HANDSHAKE_SERVER_NAME, handshake_server_procs, ARRAY_SIZE(handshake_server_procs)); if (!priv->mrpc) { pr_err("failed to register handshake\n"); ret = -ENODEV; goto out_remove_group; } return 0; out_remove_group: sysfs_remove_group(&priv->pdev->dev.kobj, &handshake_attrs_group); return 0; } /** * handshake_remove * * This function is called when the handshake driver is removed. * * @param pdev platform device * * @return 0 for success, error code otherwise */ static int handshake_remove(struct platform_device *pdev) { struct handshake_private *priv = platform_get_drvdata(pdev); mrpc_server_unregister(priv->mrpc); sysfs_remove_group(&priv->pdev->dev.kobj, &handshake_attrs_group); dev_set_drvdata(&pdev->dev, NULL); return 0; } static struct platform_driver handshake_driver = { .driver = { .name = HANDSHAKE_SERVER_NAME, }, .probe = handshake_probe, .remove = handshake_remove, }; static struct platform_device *handshake_device; static int __init handshake_server_init(void) { int ret; ret = platform_driver_register(&handshake_driver); if (ret < 0) { pr_err("Failed to register handshake platform driver: %d\n", ret); return ret; } handshake_device = platform_device_register_simple(HANDSHAKE_SERVER_NAME, -1, NULL, 0); if (IS_ERR(handshake_device)) { pr_err("Failed to register handshake platform device\n"); platform_driver_unregister(&handshake_driver); return PTR_ERR(handshake_device); } return 0; } static void __exit handshake_server_exit(void) { platform_device_unregister(handshake_device); platform_driver_unregister(&handshake_driver); } /******************************************************/ /** Module Declarations **/ /******************************************************/ module_init(handshake_server_init); module_exit(handshake_server_exit); MODULE_AUTHOR("Intel Corporation"); MODULE_AUTHOR("Tzviel Lemberger tzviel.lemberger@intel.com"); MODULE_DESCRIPTION("handshake mrpc server"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0");