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

Commit a83c0842 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents cc0af699 acff6435
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