Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c29e00f authored by Srinivas Dasari's avatar Srinivas Dasari
Browse files

msm: driver to create cld80211 nl family at bootup time



Create cnss_genl driver to create a netlink family cld80211
and make it available to cld driver and applications when
they query for it.
This driver creates multicast groups to facilitate communication
from cld driver to userspace and allows cld driver to register
for different commands from user space

Change-Id: I0795dd08b6429fad60187fee724b3fd3ccfa5603
Signed-off-by: default avatarSrinivas Dasari <dasaris@codeaurora.org>
CRs-Fixed: 1100401
parent 2f89b761
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -122,5 +122,6 @@ config CLD_LL_CORE
	 support.

source "drivers/net/wireless/cnss_utils/Kconfig"
source "drivers/net/wireless/cnss_genl/Kconfig"

endif # WLAN
+1 −0
Original line number Diff line number Diff line
@@ -29,3 +29,4 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/

obj-$(CONFIG_CNSS_UTILS) += cnss_utils/
obj-$(CONFIG_CNSS_GENL) += cnss_genl/
+7 −0
Original line number Diff line number Diff line
config CNSS_GENL
	tristate "CNSS Generic Netlink Socket Driver"
	---help---
	  This module creates generic netlink family "CLD80211". This can be
	  used by cld driver and userspace utilities to communicate over
	  netlink sockets. This module creates different multicast groups to
	  facilitate the same.
+1 −0
Original line number Diff line number Diff line
obj-$(CONFIG_CNSS_GENL) := cnss_nl.o
+204 −0
Original line number Diff line number Diff line
/* Copyright (c) 2017, The Linux Foundation. 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 and
 * only 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 <net/genetlink.h>
#include <net/cnss_nl.h>
#include <linux/module.h>

#define CLD80211_GENL_NAME "cld80211"

#define CLD80211_MULTICAST_GROUP_SVC_MSGS       "svc_msgs"
#define CLD80211_MULTICAST_GROUP_HOST_LOGS      "host_logs"
#define CLD80211_MULTICAST_GROUP_FW_LOGS        "fw_logs"
#define CLD80211_MULTICAST_GROUP_PER_PKT_STATS  "per_pkt_stats"
#define CLD80211_MULTICAST_GROUP_DIAG_EVENTS    "diag_events"
#define CLD80211_MULTICAST_GROUP_FATAL_EVENTS   "fatal_events"
#define CLD80211_MULTICAST_GROUP_OEM_MSGS       "oem_msgs"

static const struct genl_multicast_group nl_mcgrps[] = {
	[CLD80211_MCGRP_SVC_MSGS] = { .name =
			CLD80211_MULTICAST_GROUP_SVC_MSGS},
	[CLD80211_MCGRP_HOST_LOGS] = { .name =
			CLD80211_MULTICAST_GROUP_HOST_LOGS},
	[CLD80211_MCGRP_FW_LOGS] = { .name =
			CLD80211_MULTICAST_GROUP_FW_LOGS},
	[CLD80211_MCGRP_PER_PKT_STATS] = { .name =
			CLD80211_MULTICAST_GROUP_PER_PKT_STATS},
	[CLD80211_MCGRP_DIAG_EVENTS] = { .name =
			CLD80211_MULTICAST_GROUP_DIAG_EVENTS},
	[CLD80211_MCGRP_FATAL_EVENTS] = { .name =
			CLD80211_MULTICAST_GROUP_FATAL_EVENTS},
	[CLD80211_MCGRP_OEM_MSGS] = { .name =
			CLD80211_MULTICAST_GROUP_OEM_MSGS},
};

struct cld_ops {
	cld80211_cb cb;
	void *cb_ctx;
};

struct cld80211_nl_data {
	struct cld_ops cld_ops[CLD80211_MAX_COMMANDS];
};

static struct cld80211_nl_data nl_data;

static inline struct cld80211_nl_data *get_local_ctx(void)
{
	return &nl_data;
}

static struct genl_ops nl_ops[CLD80211_MAX_COMMANDS];

/* policy for the attributes */
static const struct nla_policy cld80211_policy[CLD80211_ATTR_MAX + 1] = {
	[CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED },
	[CLD80211_ATTR_DATA] = { .type = NLA_BINARY,
				 .len = CLD80211_MAX_NL_DATA },
};

static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
			     struct genl_info *info)
{
	u8 cmd_id = ops->cmd;
	struct cld80211_nl_data *nl = get_local_ctx();

	if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) {
		pr_err("CLD80211: Command Not supported: %u\n", cmd_id);
		return -EOPNOTSUPP;
	}
	info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb;
	info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx;

	return 0;
}

/* The netlink family */
static struct genl_family cld80211_fam = {
	.id = GENL_ID_GENERATE,
	.name = CLD80211_GENL_NAME,
	.hdrsize = 0,			/* no private header */
	.version = 1,			/* no particular meaning now */
	.maxattr = CLD80211_ATTR_MAX,
	.netnsok = true,
	.pre_doit = cld80211_pre_doit,
	.post_doit = NULL,
};

int register_cld_cmd_cb(u8 cmd_id, cld80211_cb func, void *cb_ctx)
{
	struct cld80211_nl_data *nl = get_local_ctx();

	pr_debug("CLD80211: Registering command: %d\n", cmd_id);
	if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
		pr_debug("CLD80211: invalid command: %d\n", cmd_id);
		return -EINVAL;
	}

	nl->cld_ops[cmd_id - 1].cb = func;
	nl->cld_ops[cmd_id - 1].cb_ctx = cb_ctx;

	return 0;
}
EXPORT_SYMBOL(register_cld_cmd_cb);

int deregister_cld_cmd_cb(u8 cmd_id)
{
	struct cld80211_nl_data *nl = get_local_ctx();

	pr_debug("CLD80211: De-registering command: %d\n", cmd_id);
	if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
		pr_debug("CLD80211: invalid command: %d\n", cmd_id);
		return -EINVAL;
	}

	nl->cld_ops[cmd_id - 1].cb = NULL;
	nl->cld_ops[cmd_id - 1].cb_ctx = NULL;

	return 0;
}
EXPORT_SYMBOL(deregister_cld_cmd_cb);

struct genl_family *cld80211_get_genl_family(void)
{
	return &cld80211_fam;
}
EXPORT_SYMBOL(cld80211_get_genl_family);

static int cld80211_doit(struct sk_buff *skb, struct genl_info *info)
{
	cld80211_cb cld_cb;
	void *cld_ctx;

	cld_cb = info->user_ptr[0];

	if (!cld_cb) {
		pr_err("CLD80211: Not supported\n");
		return -EOPNOTSUPP;
	}
	cld_ctx = info->user_ptr[1];

	if (info->attrs[CLD80211_ATTR_VENDOR_DATA]) {
		cld_cb(nla_data(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
		       nla_len(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
		       cld_ctx, info->snd_portid);
	} else {
		pr_err("CLD80211: No CLD80211_ATTR_VENDOR_DATA\n");
		return -EINVAL;
	}
	return 0;
}

static int __cld80211_init(void)
{
	int err, i;

	memset(&nl_ops[0], 0, sizeof(nl_ops));

	pr_info("CLD80211: Initializing\n");
	for (i = 0; i < CLD80211_MAX_COMMANDS; i++) {
		nl_ops[i].cmd = i + 1;
		nl_ops[i].doit = cld80211_doit;
		nl_ops[i].flags = GENL_ADMIN_PERM;
		nl_ops[i].policy = cld80211_policy;
	}

	err = genl_register_family_with_ops_groups(&cld80211_fam, nl_ops,
						   nl_mcgrps);
	if (err) {
		pr_err("CLD80211: Failed to register cld80211 family: %d\n",
		       err);
	}

	return err;
}

static void __cld80211_exit(void)
{
	genl_unregister_family(&cld80211_fam);
}

static int __init cld80211_init(void)
{
	return __cld80211_init();
}

static void __exit cld80211_exit(void)
{
	__cld80211_exit();
}

module_init(cld80211_init);
module_exit(cld80211_exit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CNSS generic netlink module");
Loading