Loading drivers/platform/msm/ipa/ipa_debugfs.c +2 −0 Original line number Diff line number Diff line Loading @@ -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[] = { Loading drivers/platform/msm/ipa/ipa_qmi_service.c +160 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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 */ Loading @@ -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); Loading Loading @@ -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"); } drivers/platform/msm/ipa/ipa_qmi_service.h +56 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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[]; Loading Loading @@ -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; Loading Loading @@ -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 */ drivers/platform/msm/ipa/rmnet_ipa.c +279 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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 */ Loading Loading @@ -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"}, {}, Loading Loading @@ -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"); Loading @@ -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; Loading drivers/platform/msm/ipa/rmnet_ipa_fd_ioctl.c +64 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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 Loading
drivers/platform/msm/ipa/ipa_debugfs.c +2 −0 Original line number Diff line number Diff line Loading @@ -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[] = { Loading
drivers/platform/msm/ipa/ipa_qmi_service.c +160 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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 */ Loading @@ -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); Loading Loading @@ -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"); }
drivers/platform/msm/ipa/ipa_qmi_service.h +56 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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[]; Loading Loading @@ -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; Loading Loading @@ -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 */
drivers/platform/msm/ipa/rmnet_ipa.c +279 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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 */ Loading Loading @@ -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"}, {}, Loading Loading @@ -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"); Loading @@ -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; Loading
drivers/platform/msm/ipa/rmnet_ipa_fd_ioctl.c +64 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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