/* * Copyright (c) 2013-2014 TRUSTONIC LIMITED * All Rights Reserved. * * 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. * * 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. */ #include #include #include #include #include #include #include #include #include #include "connection.h" #include "common.h" #define MC_DAEMON_NETLINK 17 struct mc_kernelapi_ctx { struct sock *sk; struct list_head peers; atomic_t counter; }; struct mc_kernelapi_ctx *mod_ctx; /* Define a MobiCore Kernel API device structure for use with dev_debug() etc */ struct device_driver mc_kernel_api_name = { .name = "mckernelapi" }; struct device mc_kernel_api_subname = { .init_name = "", /* Set to 'mcapi' at mcapi_init() time */ .driver = &mc_kernel_api_name }; struct device *mc_kapi = &mc_kernel_api_subname; /* get a unique ID */ unsigned int mcapi_unique_id(void) { return (unsigned int)atomic_inc_return(&(mod_ctx->counter)); } static struct connection *mcapi_find_connection(uint32_t seq) { struct connection *tmp; struct list_head *pos; /* Get session for session_id */ list_for_each(pos, &mod_ctx->peers) { tmp = list_entry(pos, struct connection, list); if (tmp->sequence_magic == seq) return tmp; } return NULL; } void mcapi_insert_connection(struct connection *connection) { list_add_tail(&(connection->list), &(mod_ctx->peers)); connection->socket_descriptor = mod_ctx->sk; } void mcapi_remove_connection(uint32_t seq) { struct connection *tmp; struct list_head *pos, *q; /* * Delete all session objects. Usually this should not be needed as * closeDevice() requires that all sessions have been closed before. */ list_for_each_safe(pos, q, &mod_ctx->peers) { tmp = list_entry(pos, struct connection, list); if (tmp->sequence_magic == seq) { list_del(pos); break; } } } static int mcapi_process(struct sk_buff *skb, struct nlmsghdr *nlh) { struct connection *c; int seq; int ret; seq = nlh->nlmsg_seq; MCDRV_DBG_VERBOSE(mc_kapi, "nlmsg len %d type %d pid 0x%X seq %d\n", nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_pid, seq); do { c = mcapi_find_connection(seq); if (!c) { MCDRV_ERROR(mc_kapi, "Invalid incoming connection - seq=%u!", seq); ret = -1; break; } /* Pass the buffer to the appropriate connection */ connection_process(c, skb); ret = 0; } while (false); return ret; } static void mcapi_callback(struct sk_buff *skb) { struct nlmsghdr *nlh = nlmsg_hdr(skb); int len = skb->len; int err = 0; while (NLMSG_OK(nlh, len)) { err = mcapi_process(skb, nlh); /* if err or if this message says it wants a response */ if (err || (nlh->nlmsg_flags & NLM_F_ACK)) netlink_ack(skb, nlh, err); nlh = NLMSG_NEXT(nlh, len); } } static int __init mcapi_init(void) { struct netlink_kernel_cfg netlink_cfg; dev_set_name(mc_kapi, "mcapi"); dev_info(mc_kapi, "Mobicore API module initialized!\n"); netlink_cfg.groups = 0; netlink_cfg.flags = 0; netlink_cfg.input = mcapi_callback; netlink_cfg.cb_mutex = NULL; netlink_cfg.bind = NULL; mod_ctx = kzalloc(sizeof(struct mc_kernelapi_ctx), GFP_KERNEL); if (mod_ctx == NULL) { MCDRV_DBG_ERROR(mc_kapi, "Allocation failure"); return -ENOMEM; } /* start kernel thread */ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, &netlink_cfg); if (!mod_ctx->sk) { MCDRV_ERROR(mc_kapi, "register of receive handler failed"); kfree(mod_ctx); mod_ctx = NULL; return -EFAULT; } INIT_LIST_HEAD(&mod_ctx->peers); return 0; } static void __exit mcapi_exit(void) { dev_info(mc_kapi, "Unloading Mobicore API module.\n"); if (mod_ctx->sk != NULL) { netlink_kernel_release(mod_ctx->sk); mod_ctx->sk = NULL; } kfree(mod_ctx); mod_ctx = NULL; } module_init(mcapi_init); module_exit(mcapi_exit); MODULE_AUTHOR("Trustonic Limited"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MobiCore API driver");