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

Commit c40b898e authored by Conner Huff's avatar Conner Huff Committed by Subash Abhinov Kasiviswanathan
Browse files

rmnet_ctl: Rmnet control driver



Added RmNet control driver to communicate with dedicated MHI channels.
The driver provides interfaces for clients to register QMAP command
handlers and transmit QMAP packets.

Change-Id: Ib70ce1e2e6e5c6248888a515bfc91e09e042c534
Acked-by: default avatarWeiyi Chen <weiyic@qti.qualcomm.com>
Signed-off-by: default avatarConner Huff <chuff@codeaurora.org>
parent 35fd0366
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -830,6 +830,7 @@ endif # MSM_PM

source "drivers/soc/qcom/memshare/Kconfig"
source "drivers/soc/qcom/hab/Kconfig"
source "drivers/soc/qcom/rmnet_ctl/Kconfig"

config MSM_PERFORMANCE
	tristate "msm performance driver to support userspace fmin/fmax request"
+1 −0
Original line number Diff line number Diff line
@@ -102,3 +102,4 @@ obj-$(CONFIG_QCOM_AOP_DDR_MESSAGING) += aop_ddr_msgs.o
obj-$(CONFIG_MSM_HAB) += hab/
obj-$(CONFIG_QCOM_HYP_CORE_CTL) += hyp_core_ctl.o
obj-$(CONFIG_QCOM_AOP_DDRSS_COMMANDS) += aop_ddrss_cmds.o
obj-$(CONFIG_RMNET_CTL) += rmnet_ctl/
+11 −0
Original line number Diff line number Diff line
#
# RMNET CTL driver
#

menuconfig RMNET_CTL
	tristate "RmNet Control driver"
	depends on MHI_BUS
	---help---
	  Enable the RMNET CTL module which is used for communicating with
	  device via map command protocol. This module will receive QMAP
	  control commands via MHI.
+7 −0
Original line number Diff line number Diff line
#
# Makefile for the RMNET CTL module
#

rmnet_ctl-y		 += rmnet_ctl_client.o
rmnet_ctl-y		 += rmnet_ctl_mhi.o
obj-$(CONFIG_RMNET_CTL) += rmnet_ctl.o
+128 −0
Original line number Diff line number Diff line
/* Copyright (c) 2019, 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 <soc/qcom/rmnet_ctl.h>
#include "rmnet_ctl_client.h"

struct rmnet_ctl_client {
	struct rmnet_ctl_client_hooks hooks;
};

struct rmnet_ctl_endpoint {
	struct rmnet_ctl_dev __rcu *dev;
	struct rmnet_ctl_client __rcu *client;
};

static DEFINE_SPINLOCK(client_lock);
static struct rmnet_ctl_endpoint ctl_ep;

void rmnet_ctl_endpoint_setdev(const struct rmnet_ctl_dev *dev)
{
	rcu_assign_pointer(ctl_ep.dev, dev);
}

void rmnet_ctl_endpoint_post(const void *data, size_t len)
{
	struct rmnet_ctl_client *client;
	struct sk_buff *skb;

	if (unlikely(!data || !len))
		return;

	rcu_read_lock();

	client = rcu_dereference(ctl_ep.client);

	if (client && client->hooks.ctl_dl_client_hook) {
		skb = alloc_skb(len, GFP_ATOMIC);
		if (skb) {
			skb_put_data(skb, data, len);
			skb->protocol = htons(ETH_P_MAP);
			client->hooks.ctl_dl_client_hook(skb);
		}
	}

	rcu_read_unlock();
}

void *rmnet_ctl_register_client(struct rmnet_ctl_client_hooks *hook)
{
	struct rmnet_ctl_client *client;

	if (!hook)
		return NULL;

	client = (struct rmnet_ctl_client *)
			kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client)
		return NULL;
	client->hooks = *hook;

	spin_lock(&client_lock);

	/* Only support one client for now */
	if (rcu_dereference(ctl_ep.client)) {
		spin_unlock(&client_lock);
		kfree(client);
		return NULL;
	}

	rcu_assign_pointer(ctl_ep.client, client);

	spin_unlock(&client_lock);

	return client;
}
EXPORT_SYMBOL(rmnet_ctl_register_client);

int rmnet_ctl_unregister_client(void *handle)
{
	struct rmnet_ctl_client *client = (struct rmnet_ctl_client *)handle;

	spin_lock(&client_lock);

	if (rcu_dereference(ctl_ep.client) != client) {
		spin_unlock(&client_lock);
		return -EINVAL;
	}

	RCU_INIT_POINTER(ctl_ep.client, NULL);

	spin_unlock(&client_lock);

	synchronize_rcu();
	kfree(client);

	return 0;
}
EXPORT_SYMBOL(rmnet_ctl_unregister_client);

int rmnet_ctl_send_client(void *handle, struct sk_buff *skb)
{
	struct rmnet_ctl_client *client = (struct rmnet_ctl_client *)handle;
	struct rmnet_ctl_dev *dev;
	int rc = -EINVAL;

	if (client != rcu_dereference(ctl_ep.client))
		return rc;

	rcu_read_lock();

	dev = rcu_dereference(ctl_ep.dev);
	if (dev && dev->xmit)
		rc = dev->xmit(dev, skb);

	rcu_read_unlock();

	return rc;
}
EXPORT_SYMBOL(rmnet_ctl_send_client);
Loading