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

Commit acff6435 authored by Ravinder Konka's avatar Ravinder Konka Committed by Sivan Reinstein
Browse files

msm: ipa: add ipa tethering stats support to IPA WAN driver



In tethering cases where IPA HW offloading is applied, it is required
from the IPA WAN driver to communicate with the IPA Q6 driver in order
to retrieve data usage statistics for the tethering pipes as well as
the network/quota usage on a specific PDN.

Change-Id: Iffc3ada159f85d6a5ddd21a444a6ad7563e725e9
Acked-by: default avatarDavid Arinzon <darinzon@qti.qualcomm.com>
Signed-off-by: default avatarSivan Reinstein <sivanr@codeaurora.org>
parent 327447f0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ const char *ipa_event_name[] = {
	__stringify(WAN_XLAT_CONNECT),
	__stringify(ECM_CONNECT),
	__stringify(ECM_DISCONNECT),
	__stringify(IPA_TETHERING_STATS_UPDATE_STATS),
	__stringify(IPA_TETHERING_STATS_UPDATE_NETWORK_STATS),
};

const char *ipa_hdr_l2_type_name[] = {
+160 −0
Original line number Diff line number Diff line
@@ -704,6 +704,39 @@ static void ipa_q6_clnt_notify(struct qmi_handle *handle,
	}
}

static void ipa_q6_clnt_ind_cb(struct qmi_handle *handle, unsigned int msg_id,
			       void *msg, unsigned int msg_len,
			       void *ind_cb_priv)
{
	struct ipa_data_usage_quota_reached_ind_msg_v01 qmi_ind;
	struct msg_desc qmi_ind_desc;
	int rc = 0;

	if (handle != ipa_q6_clnt) {
		IPAWANERR("Wrong client\n");
		return;
	}

	if (QMI_IPA_DATA_USAGE_QUOTA_REACHED_IND_V01 == msg_id) {
		memset(&qmi_ind, 0, sizeof(
			struct ipa_data_usage_quota_reached_ind_msg_v01));
		qmi_ind_desc.max_msg_len =
			QMI_IPA_DATA_USAGE_QUOTA_REACHED_IND_MAX_MSG_LEN_V01;
		qmi_ind_desc.msg_id = QMI_IPA_DATA_USAGE_QUOTA_REACHED_IND_V01;
		qmi_ind_desc.ei_array =
			ipa_data_usage_quota_reached_ind_msg_data_v01_ei;

		rc = qmi_kernel_decode(&qmi_ind_desc, &qmi_ind, msg, msg_len);
		if (rc < 0) {
			IPAWANERR("Error decoding msg_id %d\n", msg_id);
			return;
		}
		IPAWANDBG("Quota reached indication on qmux(%d) Mbytes(%lu)\n",
			  qmi_ind.apn.mux_id,
			  (unsigned long int) qmi_ind.apn.num_Mbytes);
		ipa_broadcast_quota_reach_ind(qmi_ind.apn.mux_id);
	}
}

static void ipa_q6_clnt_svc_arrive(struct work_struct *work)
{
@@ -729,6 +762,11 @@ static void ipa_q6_clnt_svc_arrive(struct work_struct *work)
		ipa_q6_clnt = NULL;
		return;
	}

	rc = qmi_register_ind_cb(ipa_q6_clnt, ipa_q6_clnt_ind_cb, NULL);
	if (rc < 0)
		IPAWANERR("Unable to register for indications\n");

	ipa_q6_clnt_reset = 0;
	IPAWANDBG("Q6 QMI service available now\n");
	/* Initialize modem IPA-driver */
@@ -750,6 +788,8 @@ static void ipa_q6_clnt_svc_arrive(struct work_struct *work)
	}
	qmi_modem_init_fin = true;
	ipa_proxy_clk_unvote();
	/* is_load_uc=FALSE indicates that SSR has occurred */
	ipa_q6_handshake_complete(!is_load_uc);
	IPAWANDBG("complete, qmi_modem_init_fin : %d\n",
		qmi_modem_init_fin);

@@ -1009,3 +1049,123 @@ int vote_for_bus_bw(uint32_t *bw_mbps)

	return ret;
}

int ipa_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req,
			   struct ipa_get_data_stats_resp_msg_v01 *resp)
{
	struct msg_desc req_desc, resp_desc;
	int rc;

	req_desc.max_msg_len = QMI_IPA_GET_DATA_STATS_REQ_MAX_MSG_LEN_V01;
	req_desc.msg_id = QMI_IPA_GET_DATA_STATS_REQ_V01;
	req_desc.ei_array = ipa_get_data_stats_req_msg_data_v01_ei;

	resp_desc.max_msg_len = QMI_IPA_GET_DATA_STATS_RESP_MAX_MSG_LEN_V01;
	resp_desc.msg_id = QMI_IPA_GET_DATA_STATS_RESP_V01;
	resp_desc.ei_array = ipa_get_data_stats_resp_msg_data_v01_ei;

	IPAWANDBG("Sending QMI_IPA_GET_DATA_STATS_REQ_V01\n");

	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
			sizeof(struct ipa_get_data_stats_req_msg_v01),
			&resp_desc, resp,
			sizeof(struct ipa_get_data_stats_resp_msg_v01),
			0);

	IPAWANDBG("QMI_IPA_GET_DATA_STATS_RESP_V01 received\n");

	return ipa_check_qmi_response(rc,
		QMI_IPA_GET_DATA_STATS_REQ_V01, resp->resp.result,
		resp->resp.error, "ipa_get_data_stats_resp_msg_v01");
}

int ipa_qmi_get_network_stats(struct ipa_get_apn_data_stats_req_msg_v01 *req,
			      struct ipa_get_apn_data_stats_resp_msg_v01 *resp)
{
	struct msg_desc req_desc, resp_desc;
	int rc;

	req_desc.max_msg_len = QMI_IPA_GET_APN_DATA_STATS_REQ_MAX_MSG_LEN_V01;
	req_desc.msg_id = QMI_IPA_GET_APN_DATA_STATS_REQ_V01;
	req_desc.ei_array = ipa_get_apn_data_stats_req_msg_data_v01_ei;

	resp_desc.max_msg_len = QMI_IPA_GET_APN_DATA_STATS_RESP_MAX_MSG_LEN_V01;
	resp_desc.msg_id = QMI_IPA_GET_APN_DATA_STATS_RESP_V01;
	resp_desc.ei_array = ipa_get_apn_data_stats_resp_msg_data_v01_ei;

	IPAWANDBG("Sending QMI_IPA_GET_APN_DATA_STATS_REQ_V01\n");

	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
			sizeof(struct ipa_get_apn_data_stats_req_msg_v01),
			&resp_desc, resp,
			sizeof(struct ipa_get_apn_data_stats_resp_msg_v01),
			0);

	IPAWANDBG("QMI_IPA_GET_APN_DATA_STATS_RESP_V01 received\n");

	return ipa_check_qmi_response(rc,
		QMI_IPA_GET_APN_DATA_STATS_REQ_V01, resp->resp.result,
		resp->resp.error, "ipa_get_apn_data_stats_req_msg_v01");
}

int ipa_qmi_set_data_quota(struct ipa_set_data_usage_quota_req_msg_v01 *req)
{
	struct ipa_set_data_usage_quota_resp_msg_v01 resp;
	struct msg_desc req_desc, resp_desc;
	int rc;

	memset(&resp, 0, sizeof(struct ipa_set_data_usage_quota_resp_msg_v01));

	req_desc.max_msg_len = QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_MAX_MSG_LEN_V01;
	req_desc.msg_id = QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01;
	req_desc.ei_array = ipa_set_data_usage_quota_req_msg_data_v01_ei;

	resp_desc.max_msg_len =
		QMI_IPA_SET_DATA_USAGE_QUOTA_RESP_MAX_MSG_LEN_V01;
	resp_desc.msg_id = QMI_IPA_SET_DATA_USAGE_QUOTA_RESP_V01;
	resp_desc.ei_array = ipa_set_data_usage_quota_resp_msg_data_v01_ei;

	IPAWANDBG("Sending QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01\n");

	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
			sizeof(struct ipa_set_data_usage_quota_req_msg_v01),
			&resp_desc, &resp, sizeof(resp), 0);

	IPAWANDBG("QMI_IPA_SET_DATA_USAGE_QUOTA_RESP_V01 received\n");

	return ipa_check_qmi_response(rc,
		QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01, resp.resp.result,
		resp.resp.error, "ipa_set_data_usage_quota_req_msg_v01");
}

int ipa_qmi_stop_data_qouta(void)
{
	struct ipa_stop_data_usage_quota_req_msg_v01 req;
	struct ipa_stop_data_usage_quota_resp_msg_v01 resp;
	struct msg_desc req_desc, resp_desc;
	int rc;

	memset(&req, 0, sizeof(struct ipa_stop_data_usage_quota_req_msg_v01));
	memset(&resp, 0, sizeof(struct ipa_stop_data_usage_quota_resp_msg_v01));

	req_desc.max_msg_len =
		QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_MAX_MSG_LEN_V01;
	req_desc.msg_id = QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_V01;
	req_desc.ei_array = ipa_stop_data_usage_quota_req_msg_data_v01_ei;

	resp_desc.max_msg_len =
		QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_MAX_MSG_LEN_V01;
	resp_desc.msg_id = QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_V01;
	resp_desc.ei_array = ipa_stop_data_usage_quota_resp_msg_data_v01_ei;

	IPAWANDBG("Sending QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_V01\n");

	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, &req, sizeof(req),
		&resp_desc, &resp, sizeof(resp), 0);

	IPAWANDBG("QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_V01 received\n");

	return ipa_check_qmi_response(rc,
		QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_V01, resp.resp.result,
		resp.resp.error, "ipa_stop_data_usage_quota_req_msg_v01");
}
+56 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <uapi/linux/msm_rmnet.h>
#include <soc/qcom/msm_qmi_interface.h>
#include "ipa_i.h"
#include <linux/rmnet_ipa_fd_ioctl.h>

/**
 * name of the DL wwan default routing tables for v4 and v6
@@ -57,6 +58,18 @@ struct rmnet_mux_val {
	uint32_t  hdr_hdl;
};

int rmnet_ipa_poll_tethering_stats(struct wan_ioctl_poll_tethering_stats *data);
int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data);
void ipa_broadcast_quota_reach_ind(uint32_t mux_id);

int ipa_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req,
	struct ipa_get_data_stats_resp_msg_v01 *resp);
int ipa_qmi_get_network_stats(struct ipa_get_apn_data_stats_req_msg_v01 *req,
	struct ipa_get_apn_data_stats_resp_msg_v01 *resp);
int ipa_qmi_set_data_quota(struct ipa_set_data_usage_quota_req_msg_v01 *req);
int ipa_qmi_stop_data_qouta(void);
void ipa_q6_handshake_complete(bool);

extern struct elem_info ipa_init_modem_driver_req_msg_data_v01_ei[];
extern struct elem_info ipa_init_modem_driver_resp_msg_data_v01_ei[];
extern struct elem_info ipa_indication_reg_req_msg_data_v01_ei[];
@@ -85,9 +98,13 @@ extern struct elem_info ipa_stop_data_usage_quota_resp_msg_data_v01_ei[];
/**
 * struct ipa_rmnet_context - IPA rmnet context
 * @ipa_rmnet_ssr: support modem SSR
 * @polling_interval: Requested interval for polling tethered statistics
 * @metered_mux_id: The mux ID on which quota has been set
 */
struct ipa_rmnet_context {
	bool ipa_rmnet_ssr;
	u64 polling_interval;
	u32 metered_mux_id;
};

extern struct ipa_rmnet_context ipa_rmnet_ctx;
@@ -207,6 +224,45 @@ static inline int vote_for_bus_bw(uint32_t *bw_mbps)
	return -EPERM;
}

int rmnet_ipa_poll_tethering_stats(struct wan_ioctl_poll_tethering_stats *data)
{
	return -EPERM;
}

int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data)
{
	return -EPERM;
}

void ipa_broadcast_quota_reach_ind(uint8_t mux_id)
{
}

int ipa_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req,
	struct ipa_get_data_stats_resp_msg_v01 *resp);
{
	return -EPERM;
}

int ipa_qmi_get_network_stats(struct ipa_get_apn_data_stats_req_msg_v01 *req,
	struct ipa_get_apn_data_stats_resp_msg_v01 *resp);
{
	return -EPERM;
}

int ipa_qmi_set_data_quota(struct ipa_set_network_quota_req_msg_v01 *req)
{
	return -EPERM;
}

int ipa_qmi_stop_data_qouta(void)
{
	return -EPERM;
}

void ipa_q6_handshake_complete(bool)
{
}
#endif /* CONFIG_RMNET_IPA */

#endif /* IPA_QMI_SERVICE_H */
+279 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
#include "ipa_qmi_service.h"
#include <linux/rmnet_ipa_fd_ioctl.h>

#define WWAN_METADATA_SHFT 24
#define WWAN_METADATA_MASK 0xFF000000
@@ -48,6 +49,11 @@

#define IPA_WWAN_RX_SOFTIRQ_THRESH 16

#define INVALID_MUX_ID 0xFF
#define IPA_QUOTA_REACH_ALERT_MAX_SIZE 64
#define IPA_QUOTA_REACH_IF_NAME_MAX_SIZE 64
#define IPA_UEVENT_NUM_EVNP 3 /* number of event pointers */

static struct net_device *ipa_netdevs[IPA_WWAN_DEVICE_COUNT];
static struct ipa_sys_connect_params apps_to_ipa_ep_cfg, ipa_to_apps_ep_cfg;
static u32 qmap_hdr_hdl, dflt_v4_wan_rt_hdl, dflt_v6_wan_rt_hdl;
@@ -68,6 +74,10 @@ static void ipa_wwan_msg_free_cb(void*, u32, u32);
static void wake_tx_queue(struct work_struct *work);
static DECLARE_WORK(ipa_tx_wakequeue_work, wake_tx_queue);

static void tethering_stats_poll_queue(struct work_struct *work);
static DECLARE_DELAYED_WORK(ipa_tether_stats_poll_wakequeue_work,
			    tethering_stats_poll_queue);

enum wwan_device_status {
	WWAN_DEVICE_INACTIVE = 0,
	WWAN_DEVICE_ACTIVE   = 1
@@ -713,6 +723,16 @@ static int find_mux_channel_index(uint32_t mux_id)
	return MAX_NUM_OF_MUX_CHANNEL;
}

static int find_vchannel_name_index(const char *vchannel_name)
{
	int i;

	for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) {
		if (0 == strcmp(mux_channel[i].vchannel_name, vchannel_name))
			return i;
	}
	return MAX_NUM_OF_MUX_CHANNEL;
}

static int wwan_register_to_ipa(int index)
{
@@ -2013,6 +2033,7 @@ static int ipa_wwan_remove(struct platform_device *pdev)
		IPAWANERR("Error deleting resource %d, ret=%d\n",
		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
	cancel_work_sync(&ipa_tx_wakequeue_work);
	cancel_delayed_work_sync(&ipa_tether_stats_poll_wakequeue_work);
	free_netdev(ipa_netdevs[0]);
	ipa_netdevs[0] = NULL;
	/* No need to remove wwan_ioctl during SSR */
@@ -2088,6 +2109,12 @@ static int rmnet_ipa_ap_resume(struct device *dev)
	return 0;
}

static void ipa_stop_polling_stats(void)
{
	cancel_delayed_work(&ipa_tether_stats_poll_wakequeue_work);
	ipa_rmnet_ctx.polling_interval = 0;
}

static const struct of_device_id rmnet_ipa_dt_match[] = {
	{.compatible = "qcom,rmnet-ipa"},
	{},
@@ -2122,6 +2149,8 @@ static int ssr_notifier_cb(struct notifier_block *this,
			netif_stop_queue(ipa_netdevs[0]);
			ipa_qmi_stop_workqueues();
			wan_ioctl_stop_qmi_messages();
			ipa_stop_polling_stats();
			atomic_set(&is_ssr, 1);
			if (atomic_read(&is_initialized))
				platform_driver_unregister(&rmnet_ipa_driver);
			pr_info("IPA BEFORE_SHUTDOWN handling is complete\n");
@@ -2145,6 +2174,256 @@ static int ssr_notifier_cb(struct notifier_block *this,
	return NOTIFY_DONE;
}

/**
 * rmnet_ipa_free_msg() - Free the msg sent to user space via ipa_send_msg
 * @buff: pointer to buffer containing the message
 * @len: message len
 * @type: message type
 *
 * This function is invoked when ipa_send_msg is complete (Provided as a
 * free function pointer along with the message).
 */
static void rmnet_ipa_free_msg(void *buff, u32 len, u32 type)
{
	if (!buff) {
		IPAWANERR("Null buffer\n");
		return;
	}

	if (type != IPA_TETHERING_STATS_UPDATE_STATS &&
		type != IPA_TETHERING_STATS_UPDATE_NETWORK_STATS) {
			IPAWANERR("Wrong type given. buff %p type %d\n",
				  buff, type);
	}
	kfree(buff);
}

/**
 * rmnet_ipa_get_stats_and_update() - Gets pipe stats from Modem
 *
 * This function queries the IPA Modem driver for the pipe stats
 * via QMI, and updates the user space IPA entity.
 */
static void rmnet_ipa_get_stats_and_update(void)
{
	struct ipa_get_data_stats_req_msg_v01 req;
	struct ipa_get_data_stats_resp_msg_v01 *resp;
	struct ipa_msg_meta msg_meta;
	int rc;

	resp = kzalloc(sizeof(struct ipa_get_data_stats_resp_msg_v01),
		       GFP_KERNEL);
	if (!resp) {
		IPAWANERR("Can't allocate memory for stats message\n");
		return;
	}

	memset(&req, 0, sizeof(struct ipa_get_data_stats_req_msg_v01));
	memset(resp, 0, sizeof(struct ipa_get_data_stats_resp_msg_v01));

	req.ipa_stats_type = QMI_IPA_STATS_TYPE_PIPE_V01;

	rc = ipa_qmi_get_data_stats(&req, resp);

	if (!rc) {
		memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
		msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_STATS;
		msg_meta.msg_len =
			sizeof(struct ipa_get_data_stats_resp_msg_v01);
		rc = ipa_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
		if (rc) {
			IPAWANERR("ipa_send_msg failed: %d\n", rc);
			kfree(resp);
			return;
		}
	}
}

/**
 * tethering_stats_poll_queue() - Stats polling function
 * @work - Work entry
 *
 * This function is scheduled periodically (per the interval) in
 * order to poll the IPA Modem driver for the pipe stats.
 */
static void tethering_stats_poll_queue(struct work_struct *work)
{
	rmnet_ipa_get_stats_and_update();

	schedule_delayed_work(&ipa_tether_stats_poll_wakequeue_work,
			msecs_to_jiffies(ipa_rmnet_ctx.polling_interval*1000));
}

/**
 * rmnet_ipa_get_network_stats_and_update() - Get network stats from IPA Modem
 *
 * This function retrieves the data usage (used quota) from the IPA Modem driver
 * via QMI, and updates IPA user space entity.
 */
static void rmnet_ipa_get_network_stats_and_update(void)
{
	struct ipa_get_apn_data_stats_req_msg_v01 req;
	struct ipa_get_apn_data_stats_resp_msg_v01 *resp;
	struct ipa_msg_meta msg_meta;
	int rc;

	resp = kzalloc(sizeof(struct ipa_get_apn_data_stats_resp_msg_v01),
		       GFP_KERNEL);
	if (!resp) {
		IPAWANERR("Can't allocate memory for network stats message\n");
		return;
	}

	memset(&req, 0, sizeof(struct ipa_get_apn_data_stats_req_msg_v01));
	memset(resp, 0, sizeof(struct ipa_get_apn_data_stats_resp_msg_v01));

	req.mux_id_list_valid = true;
	req.mux_id_list_len = 1;
	req.mux_id_list[0] = ipa_rmnet_ctx.metered_mux_id;

	rc = ipa_qmi_get_network_stats(&req, resp);

	if (!rc) {
		memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
		msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_NETWORK_STATS;
		msg_meta.msg_len =
			sizeof(struct ipa_get_apn_data_stats_resp_msg_v01);
		rc = ipa_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
		if (rc) {
			IPAWANERR("ipa_send_msg failed: %d\n", rc);
			kfree(resp);
			return;
		}
	}
}

/**
 * rmnet_ipa_poll_tethering_stats() - Tethering stats polling IOCTL handler
 * @data - IOCTL data
 *
 * This function handles WAN_IOC_POLL_TETHERING_STATS.
 * In case polling interval received is 0, polling will stop
 * (If there's a polling in progress, it will allow it to finish), and then will
 * fetch network stats, and update the IPA user space.
 *
 * Return codes:
 * 0: Success
 */
int rmnet_ipa_poll_tethering_stats(struct wan_ioctl_poll_tethering_stats *data)
{
	ipa_rmnet_ctx.polling_interval = data->polling_interval_secs;

	cancel_delayed_work_sync(&ipa_tether_stats_poll_wakequeue_work);

	if (0 == ipa_rmnet_ctx.polling_interval) {
		ipa_qmi_stop_data_qouta();
		rmnet_ipa_get_network_stats_and_update();
		rmnet_ipa_get_stats_and_update();
		return 0;
	}

	schedule_delayed_work(&ipa_tether_stats_poll_wakequeue_work, 0);
	return 0;
}

/**
 * rmnet_ipa_set_data_quota() - Data quota setting IOCTL handler
 * @data - IOCTL data
 *
 * This function handles WAN_IOC_SET_DATA_QUOTA.
 * It translates the given interface name to the Modem MUX ID and
 * sends the request of the quota to the IPA Modem driver via QMI.
 *
 * Return codes:
 * 0: Success
 * -EFAULT: Invalid interface name provided
 * other: See ipa_qmi_set_data_quota
 */
int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data)
{
	u32 mux_id;
	int index;
	struct ipa_set_data_usage_quota_req_msg_v01 req;

	index = find_vchannel_name_index(data->interface_name);

	if (index == MAX_NUM_OF_MUX_CHANNEL) {
		IPAWANERR("%s is an invalid iface name\n",
			  data->interface_name);
		return -EFAULT;
	}

	mux_id = mux_channel[index].mux_id;

	ipa_rmnet_ctx.metered_mux_id = mux_id;

	memset(&req, 0, sizeof(struct ipa_set_data_usage_quota_req_msg_v01));
	req.apn_quota_list_valid = true;
	req.apn_quota_list_len = 1;
	req.apn_quota_list[0].mux_id = mux_id;
	req.apn_quota_list[0].num_Mbytes = data->quota_mbytes;

	return ipa_qmi_set_data_quota(&req);
}

/**
 * ipa_broadcast_quota_reach_ind() - Send Netlink broadcast on Quota
 * @mux_id - The MUX ID on which the quota has been reached
 *
 * This function broadcasts a Netlink event using the kobject of the
 * rmnet_ipa interface in order to alert the user space that the quota
 * on the specific interface which matches the mux_id has been reached.
 *
 */
void ipa_broadcast_quota_reach_ind(u32 mux_id)
{
	char alert_msg[IPA_QUOTA_REACH_ALERT_MAX_SIZE];
	char iface_name[IPA_QUOTA_REACH_IF_NAME_MAX_SIZE];
	char *envp[IPA_UEVENT_NUM_EVNP] = { alert_msg, iface_name, NULL };
	int res;
	int index;

	index = find_mux_channel_index(mux_id);

	if (index == MAX_NUM_OF_MUX_CHANNEL) {
		IPAWANERR("%u is an mux ID\n", mux_id);
		return;
	}

	res = snprintf(alert_msg, IPA_QUOTA_REACH_ALERT_MAX_SIZE,
			"ALERT_NAME=%s", "quotaReachedAlert");
	if (IPA_QUOTA_REACH_ALERT_MAX_SIZE <= res) {
		IPAWANERR("message too long (%d)", res);
		return;
	}

	res = snprintf(iface_name, IPA_QUOTA_REACH_IF_NAME_MAX_SIZE,
		       "INTERFACE=%s", mux_channel[index].vchannel_name);
	if (IPA_QUOTA_REACH_IF_NAME_MAX_SIZE <= res) {
		IPAWANERR("message too long (%d)", res);
		return;
	}

	IPAWANDBG("putting nlmsg: <%s> <%s>\n", alert_msg, iface_name);
	kobject_uevent_env(&(ipa_netdevs[0]->dev.kobj), KOBJ_CHANGE, envp);
}

/**
 * ipa_q6_handshake_complete() - Perform operations once Q6 is up
 * @ssr_bootup - Indicates whether this is a cold boot-up or post-SSR.
 *
 * This function is invoked once the handshake between the IPA AP driver
 * and IPA Q6 driver is complete. At this point, it is possible to perform
 * operations which can't be performed until IPA Q6 driver is up.
 *
 */
void ipa_q6_handshake_complete(bool ssr_bootup)
{
	/* It is required to recover the network stats after SSR recovery */
	if (ssr_bootup)
		rmnet_ipa_get_network_stats_and_update();
}

static int __init ipa_wwan_init(void)
{
	void *subsys;
+64 −2
Original line number Diff line number Diff line
@@ -29,6 +29,12 @@
#define WAN_IOC_ADD_FLT_RULE_INDEX32 _IOWR(WAN_IOC_MAGIC, \
		WAN_IOCTL_ADD_FLT_INDEX, \
		compat_uptr_t)
#define WAN_IOC_POLL_TETHERING_STATS32 _IOWR(WAN_IOC_MAGIC, \
		WAN_IOCTL_POLL_TETHERING_STATS, \
		compat_uptr_t)
#define WAN_IOC_SET_DATA_QUOTA32 _IOWR(WAN_IOC_MAGIC, \
		WAN_IOCTL_SET_DATA_QUOTA, \
		compat_uptr_t)
#endif

static unsigned int dev_num = 1;
@@ -126,6 +132,56 @@ static long wan_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
		}
		break;

	case WAN_IOC_POLL_TETHERING_STATS:
		IPAWANDBG("device %s got WAN_IOCTL_POLL_TETHERING_STATS :>>>\n",
			  DRIVER_NAME);
		pyld_sz = sizeof(struct wan_ioctl_poll_tethering_stats);
		param = kzalloc(pyld_sz, GFP_KERNEL);
		if (!param) {
			retval = -ENOMEM;
			break;
		}
		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
			retval = -EFAULT;
			break;
		}
		if (rmnet_ipa_poll_tethering_stats(
		(struct wan_ioctl_poll_tethering_stats *)param)) {
			IPAWANERR("WAN_IOCTL_POLL_TETHERING_STATS failed\n");
			retval = -EFAULT;
			break;
		}
		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
			retval = -EFAULT;
			break;
		}
		break;

	case WAN_IOC_SET_DATA_QUOTA:
		IPAWANDBG("device %s got WAN_IOCTL_SET_DATA_QUOTA :>>>\n",
			  DRIVER_NAME);
		pyld_sz = sizeof(struct wan_ioctl_set_data_quota);
		param = kzalloc(pyld_sz, GFP_KERNEL);
		if (!param) {
			retval = -ENOMEM;
			break;
		}
		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
			retval = -EFAULT;
			break;
		}
		if (rmnet_ipa_set_data_quota(
		(struct wan_ioctl_set_data_quota *)param)) {
			IPAWANERR("WAN_IOC_SET_DATA_QUOTA failed\n");
			retval = -EFAULT;
			break;
		}
		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
			retval = -EFAULT;
			break;
		}
		break;

	default:
		retval = -ENOTTY;
	}
@@ -143,6 +199,12 @@ long compat_wan_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
	case WAN_IOC_ADD_FLT_RULE_INDEX32:
		cmd = WAN_IOC_ADD_FLT_RULE_INDEX;
		break;
	case WAN_IOC_POLL_TETHERING_STATS32:
		cmd = WAN_IOC_POLL_TETHERING_STATS;
		break;
	case WAN_IOC_SET_DATA_QUOTA32:
		cmd = WAN_IOC_SET_DATA_QUOTA;
		break;
	default:
		return -ENOIOCTLCMD;
	}
Loading