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

Commit 8c3faed5 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 5cad42a8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -207,6 +207,8 @@ config QCOM_QMI_DFC
	  status indication and disables flows while grant size is reached.
	  If unsure or not use burst mode flow control, say 'N'.

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

config QCOM_QMI_POWER_COLLAPSE
	bool "Enable power save features"
	depends on QCOM_QMI_RMNET
+1 −0
Original line number Diff line number Diff line
@@ -86,3 +86,4 @@ obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o
obj-$(CONFIG_ICNSS) += icnss.o
obj-$(CONFIG_ICNSS_QMI) += icnss_qmi.o wlan_firmware_service_v01.o
obj-$(CONFIG_RMNET_CTL) += rmnet_ctl/
+12 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
#
# 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.
+8 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
#
# 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
+122 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * RMNET_CTL client handlers
 *
 */

#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 = 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