blob: 405f84058c6379f2194e4123583c9dd269a26924 [file] [log] [blame]
/*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/netlink.h>
#include <linux/kthread.h>
#include <linux/device.h>
#include <net/sock.h>
#include <linux/list.h>
#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");