Loading drivers/platform/msm/ipa/ipa_api.c +51 −0 Original line number Diff line number Diff line Loading @@ -2823,6 +2823,57 @@ void ipa_ntn_uc_dereg_rdyCB(void) } EXPORT_SYMBOL(ipa_ntn_uc_dereg_rdyCB); /** * ipa_conn_wdi3_pipes() - connect wdi3 pipes */ int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out) { int ret; IPA_API_DISPATCH_RETURN(ipa_conn_wdi3_pipes, in, out); return ret; } /** * ipa_disconn_wdi3_pipes() - disconnect wdi3 pipes */ int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { int ret; IPA_API_DISPATCH_RETURN(ipa_disconn_wdi3_pipes, ipa_ep_idx_tx, ipa_ep_idx_rx); return ret; } /** * ipa_enable_wdi3_pipes() - enable wdi3 pipes */ int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { int ret; IPA_API_DISPATCH_RETURN(ipa_enable_wdi3_pipes, ipa_ep_idx_tx, ipa_ep_idx_rx); return ret; } /** * ipa_disable_wdi3_pipes() - disable wdi3 pipes */ int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { int ret; IPA_API_DISPATCH_RETURN(ipa_disable_wdi3_pipes, ipa_ep_idx_tx, ipa_ep_idx_rx); return ret; } static const struct dev_pm_ops ipa_pm_ops = { .suspend_noirq = ipa_ap_suspend, Loading drivers/platform/msm/ipa/ipa_api.h +13 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #include <linux/ipa_mhi.h> #include <linux/ipa_uc_offload.h> #include <linux/ipa_wdi3.h> #include "ipa_common_i.h" #ifndef _IPA_API_H_ Loading Loading @@ -378,6 +379,18 @@ struct ipa_api_controller { void *user_data); void (*ipa_ntn_uc_dereg_rdyCB)(void); int (*ipa_conn_wdi3_pipes)(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out); int (*ipa_disconn_wdi3_pipes)(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int (*ipa_enable_wdi3_pipes)(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int (*ipa_disable_wdi3_pipes)(int ipa_ep_idx_tx, int ipa_ep_idx_rx); }; #ifdef CONFIG_IPA Loading drivers/platform/msm/ipa/ipa_clients/Makefile +2 −2 Original line number Diff line number Diff line obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c 0 → 100644 +526 −0 Original line number Diff line number Diff line /* Copyright (c) 2017 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 <linux/ipa_wdi3.h> #include <linux/msm_ipa.h> #include <linux/string.h> #include "../ipa_common_i.h" #define OFFLOAD_DRV_NAME "ipa_wdi3" #define IPA_WDI3_DBG(fmt, args...) \ do { \ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \ __func__, __LINE__, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) #define IPA_WDI3_LOW(fmt, args...) \ do { \ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \ __func__, __LINE__, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) #define IPA_WDI3_ERR(fmt, args...) \ do { \ pr_err(OFFLOAD_DRV_NAME " %s:%d " fmt, \ __func__, __LINE__, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) struct ipa_wdi3_intf_info { char netdev_name[IPA_RESOURCE_NAME_MAX]; u8 hdr_len; u32 partial_hdr_hdl[IPA_IP_MAX]; struct list_head link; }; struct ipa_wdi3_context { struct list_head head_intf_list; ipa_notify_cb notify; void *priv; struct completion wdi3_completion; struct mutex lock; }; static struct ipa_wdi3_context *ipa_wdi3_ctx; static int ipa_wdi3_commit_partial_hdr( struct ipa_ioc_add_hdr *hdr, const char *netdev_name, struct ipa_wdi3_hdr_info *hdr_info) { int i; if (!hdr || !hdr_info || !netdev_name) { IPA_WDI3_ERR("Invalid input\n"); return -EINVAL; } hdr->commit = 1; hdr->num_hdrs = 2; snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name), "%s_ipv4", netdev_name); snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name), "%s_ipv6", netdev_name); for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) { hdr->hdr[i].hdr_len = hdr_info[i].hdr_len; memcpy(hdr->hdr[i].hdr, hdr_info[i].hdr, hdr->hdr[i].hdr_len); hdr->hdr[i].type = hdr_info[i].hdr_type; hdr->hdr[i].is_partial = 1; hdr->hdr[i].is_eth2_ofst_valid = 1; hdr->hdr[i].eth2_ofst = hdr_info[i].dst_mac_addr_offset; } if (ipa_add_hdr(hdr)) { IPA_WDI3_ERR("fail to add partial headers\n"); return -EFAULT; } return 0; } int ipa_wdi3_reg_intf(struct ipa_wdi3_reg_intf_in_params *in) { struct ipa_ioc_add_hdr *hdr; struct ipa_wdi3_intf_info *new_intf; struct ipa_wdi3_intf_info *entry; struct ipa_tx_intf tx; struct ipa_rx_intf rx; struct ipa_ioc_tx_intf_prop tx_prop[2]; struct ipa_ioc_rx_intf_prop rx_prop[2]; u32 len; int ret = 0; if (in == NULL) { IPA_WDI3_ERR("invalid params in=%pK\n", in); return -EINVAL; } if (!ipa_wdi3_ctx) { ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL); if (ipa_wdi3_ctx == NULL) { IPA_WDI3_ERR("fail to alloc wdi3 ctx\n"); return -EFAULT; } mutex_init(&ipa_wdi3_ctx->lock); INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list); } IPA_WDI3_DBG("register interface for netdev %s\n", in->netdev_name); mutex_lock(&ipa_wdi3_ctx->lock); list_for_each_entry(entry, &ipa_wdi3_ctx->head_intf_list, link) if (strcmp(entry->netdev_name, in->netdev_name) == 0) { IPA_WDI3_DBG("intf was added before.\n"); mutex_unlock(&ipa_wdi3_ctx->lock); return 0; } IPA_WDI3_DBG("intf was not added before, proceed.\n"); new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL); if (new_intf == NULL) { IPA_WDI3_ERR("fail to alloc new intf\n"); mutex_unlock(&ipa_wdi3_ctx->lock); return -EFAULT; } INIT_LIST_HEAD(&new_intf->link); strlcpy(new_intf->netdev_name, in->netdev_name, sizeof(new_intf->netdev_name)); new_intf->hdr_len = in->hdr_info[0].hdr_len; /* add partial header */ len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_WDI3_ERR("fail to alloc %d bytes\n", len); ret = -EFAULT; goto fail_alloc_hdr; } if (ipa_wdi3_commit_partial_hdr(hdr, in->netdev_name, in->hdr_info)) { IPA_WDI3_ERR("fail to commit partial headers\n"); ret = -EFAULT; goto fail_commit_hdr; } new_intf->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl; new_intf->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl; IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", hdr->hdr[IPA_IP_v4].hdr_hdl, hdr->hdr[IPA_IP_v6].hdr_hdl); /* populate tx prop */ tx.num_props = 2; tx.prop = tx_prop; memset(tx_prop, 0, sizeof(tx_prop)); tx_prop[0].ip = IPA_IP_v4; tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS; tx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type; strlcpy(tx_prop[0].hdr_name, hdr->hdr[IPA_IP_v4].name, sizeof(tx_prop[0].hdr_name)); tx_prop[1].ip = IPA_IP_v6; tx_prop[1].dst_pipe = IPA_CLIENT_WLAN1_CONS; tx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type; strlcpy(tx_prop[1].hdr_name, hdr->hdr[IPA_IP_v6].name, sizeof(tx_prop[1].hdr_name)); /* populate rx prop */ rx.num_props = 2; rx.prop = rx_prop; memset(rx_prop, 0, sizeof(rx_prop)); rx_prop[0].ip = IPA_IP_v4; rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD; rx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type; if (in->is_meta_data_valid) { rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[0].attrib.meta_data = in->meta_data; rx_prop[0].attrib.meta_data_mask = in->meta_data_mask; } rx_prop[1].ip = IPA_IP_v6; rx_prop[1].src_pipe = IPA_CLIENT_WLAN1_PROD; rx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type; if (in->is_meta_data_valid) { rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[1].attrib.meta_data = in->meta_data; rx_prop[1].attrib.meta_data_mask = in->meta_data_mask; } if (ipa_register_intf(in->netdev_name, &tx, &rx)) { IPA_WDI3_ERR("fail to add interface prop\n"); ret = -EFAULT; goto fail_commit_hdr; } list_add(&new_intf->link, &ipa_wdi3_ctx->head_intf_list); init_completion(&ipa_wdi3_ctx->wdi3_completion); kfree(hdr); mutex_unlock(&ipa_wdi3_ctx->lock); return 0; fail_commit_hdr: kfree(hdr); fail_alloc_hdr: kfree(new_intf); mutex_unlock(&ipa_wdi3_ctx->lock); return ret; } EXPORT_SYMBOL(ipa_wdi3_reg_intf); int ipa_wdi3_dereg_intf(const char *netdev_name) { int len, ret = 0; struct ipa_ioc_del_hdr *hdr = NULL; struct ipa_wdi3_intf_info *entry; struct ipa_wdi3_intf_info *next; if (!netdev_name) { IPA_WDI3_ERR("no netdev name.\n"); return -EINVAL; } if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized.\n"); return -EPERM; } mutex_lock(&ipa_wdi3_ctx->lock); list_for_each_entry_safe(entry, next, &ipa_wdi3_ctx->head_intf_list, link) if (strcmp(entry->netdev_name, netdev_name) == 0) { len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_WDI3_ERR("fail to alloc %d bytes\n", len); mutex_unlock(&ipa_wdi3_ctx->lock); return -ENOMEM; } hdr->commit = 1; hdr->num_hdls = 2; hdr->hdl[0].hdl = entry->partial_hdr_hdl[0]; hdr->hdl[1].hdl = entry->partial_hdr_hdl[1]; IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", hdr->hdl[0].hdl, hdr->hdl[1].hdl); if (ipa_del_hdr(hdr)) { IPA_WDI3_ERR("fail to delete partial header\n"); ret = -EFAULT; goto fail; } if (ipa_deregister_intf(entry->netdev_name)) { IPA_WDI3_ERR("fail to del interface props\n"); ret = -EFAULT; goto fail; } list_del(&entry->link); kfree(entry); break; } fail: kfree(hdr); mutex_unlock(&ipa_wdi3_ctx->lock); return ret; } EXPORT_SYMBOL(ipa_wdi3_dereg_intf); static void ipa_wdi3_rm_notify(void *user_data, enum ipa_rm_event event, unsigned long data) { if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("Invalid context\n"); return; } switch (event) { case IPA_RM_RESOURCE_GRANTED: complete_all(&ipa_wdi3_ctx->wdi3_completion); break; case IPA_RM_RESOURCE_RELEASED: break; default: IPA_WDI3_ERR("Invalid RM Evt: %d", event); break; } } static int ipa_wdi3_cons_release(void) { return 0; } static int ipa_wdi3_cons_request(void) { int ret = 0; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized\n"); ret = -EFAULT; } return ret; } int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out) { int ret = 0; struct ipa_rm_create_params param; if (!(in && out)) { IPA_WDI3_ERR("empty parameters. in=%pK out=%pK\n", in, out); return -EINVAL; } if (!ipa_wdi3_ctx) { ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL); if (ipa_wdi3_ctx == NULL) { IPA_WDI3_ERR("fail to alloc wdi3 ctx\n"); return -EFAULT; } mutex_init(&ipa_wdi3_ctx->lock); INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list); } ipa_wdi3_ctx->notify = in->notify; ipa_wdi3_ctx->priv = in->priv; memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_WLAN_PROD; param.reg_params.user_data = ipa_wdi3_ctx; param.reg_params.notify_cb = ipa_wdi3_rm_notify; param.floor_voltage = IPA_VOLTAGE_SVS; ret = ipa_rm_create_resource(¶m); if (ret) { IPA_WDI3_ERR("fail to create WLAN_PROD resource\n"); return -EFAULT; } memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_WLAN_CONS; param.request_resource = ipa_wdi3_cons_request; param.release_resource = ipa_wdi3_cons_release; ret = ipa_rm_create_resource(¶m); if (ret) { IPA_WDI3_ERR("fail to create WLAN_CONS resource\n"); goto fail_create_rm_cons; } if (ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_WDI3_ERR("fail to add rm dependency\n"); ret = -EFAULT; goto fail; } if (ipa_conn_wdi3_pipes(in, out)) { IPA_WDI3_ERR("fail to setup wdi3 pipes\n"); ret = -EFAULT; goto fail; } return 0; fail: ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS); fail_create_rm_cons: ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD); return ret; } EXPORT_SYMBOL(ipa_wdi3_conn_pipes); int ipa_wdi3_disconn_pipes(void) { int ipa_ep_idx_rx, ipa_ep_idx_tx; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized\n"); return -EPERM; } ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); if (ipa_disconn_wdi3_pipes(ipa_ep_idx_rx, ipa_ep_idx_tx)) { IPA_WDI3_ERR("fail to tear down wdi3 pipes\n"); return -EFAULT; } if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_WLAN_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_WDI3_ERR("fail to delete rm dependency\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD)) { IPA_WDI3_ERR("fail to delete WLAN_PROD resource\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS)) { IPA_WDI3_ERR("fail to delete WLAN_CONS resource\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_disconn_pipes); int ipa_wdi3_enable_pipes(void) { int ret; int ipa_ep_idx_tx, ipa_ep_idx_rx; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized.\n"); return -EPERM; } ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); if (ipa_enable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) { IPA_WDI3_ERR("fail to enable wdi3 pipes\n"); return -EFAULT; } ret = ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD); if (ret == -EINPROGRESS) { if (wait_for_completion_timeout(&ipa_wdi3_ctx->wdi3_completion, 10*HZ) == 0) { IPA_WDI3_ERR("WLAN_PROD resource req time out\n"); return -EFAULT; } } else if (ret != 0) { IPA_WDI3_ERR("fail to request resource\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_enable_pipes); int ipa_wdi3_disable_pipes(void) { int ret; int ipa_ep_idx_tx, ipa_ep_idx_rx; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized.\n"); return -EPERM; } ret = ipa_rm_release_resource(IPA_RM_RESOURCE_WLAN_PROD); if (ret != 0) { IPA_WDI3_ERR("fail to release resource\n"); return -EFAULT; } ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); if (ipa_disable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) { IPA_WDI3_ERR("fail to disable wdi3 pipes\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_disable_pipes); int ipa_wdi3_set_perf_profile(struct ipa_wdi3_perf_profile *profile) { struct ipa_rm_perf_profile rm_profile; enum ipa_rm_resource_name resource_name; if (profile == NULL) { IPA_WDI3_ERR("Invalid input\n"); return -EINVAL; } rm_profile.max_supported_bandwidth_mbps = profile->max_supported_bw_mbps; if (profile->client == IPA_CLIENT_WLAN1_PROD) { resource_name = IPA_RM_RESOURCE_WLAN_PROD; } else if (profile->client == IPA_CLIENT_WLAN1_CONS) { resource_name = IPA_RM_RESOURCE_WLAN_CONS; } else { IPA_WDI3_ERR("not supported\n"); return -EINVAL; } if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) { IPA_WDI3_ERR("fail to setup rm perf profile\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_set_perf_profile); drivers/platform/msm/ipa/ipa_common_i.h +11 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/ipc_logging.h> #include <linux/ipa.h> #include <linux/ipa_uc_offload.h> #include <linux/ipa_wdi3.h> #define __FILENAME__ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) Loading Loading @@ -375,6 +376,16 @@ int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); int ipa_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *user_data), void *user_data); void ipa_ntn_uc_dereg_rdyCB(void); int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out); int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); const char *ipa_get_version_string(enum ipa_hw_type ver); int ipa_start_gsi_channel(u32 clnt_hdl); Loading Loading
drivers/platform/msm/ipa/ipa_api.c +51 −0 Original line number Diff line number Diff line Loading @@ -2823,6 +2823,57 @@ void ipa_ntn_uc_dereg_rdyCB(void) } EXPORT_SYMBOL(ipa_ntn_uc_dereg_rdyCB); /** * ipa_conn_wdi3_pipes() - connect wdi3 pipes */ int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out) { int ret; IPA_API_DISPATCH_RETURN(ipa_conn_wdi3_pipes, in, out); return ret; } /** * ipa_disconn_wdi3_pipes() - disconnect wdi3 pipes */ int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { int ret; IPA_API_DISPATCH_RETURN(ipa_disconn_wdi3_pipes, ipa_ep_idx_tx, ipa_ep_idx_rx); return ret; } /** * ipa_enable_wdi3_pipes() - enable wdi3 pipes */ int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { int ret; IPA_API_DISPATCH_RETURN(ipa_enable_wdi3_pipes, ipa_ep_idx_tx, ipa_ep_idx_rx); return ret; } /** * ipa_disable_wdi3_pipes() - disable wdi3 pipes */ int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) { int ret; IPA_API_DISPATCH_RETURN(ipa_disable_wdi3_pipes, ipa_ep_idx_tx, ipa_ep_idx_rx); return ret; } static const struct dev_pm_ops ipa_pm_ops = { .suspend_noirq = ipa_ap_suspend, Loading
drivers/platform/msm/ipa/ipa_api.h +13 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #include <linux/ipa_mhi.h> #include <linux/ipa_uc_offload.h> #include <linux/ipa_wdi3.h> #include "ipa_common_i.h" #ifndef _IPA_API_H_ Loading Loading @@ -378,6 +379,18 @@ struct ipa_api_controller { void *user_data); void (*ipa_ntn_uc_dereg_rdyCB)(void); int (*ipa_conn_wdi3_pipes)(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out); int (*ipa_disconn_wdi3_pipes)(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int (*ipa_enable_wdi3_pipes)(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int (*ipa_disable_wdi3_pipes)(int ipa_ep_idx_tx, int ipa_ep_idx_rx); }; #ifdef CONFIG_IPA Loading
drivers/platform/msm/ipa/ipa_clients/Makefile +2 −2 Original line number Diff line number Diff line obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c 0 → 100644 +526 −0 Original line number Diff line number Diff line /* Copyright (c) 2017 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 <linux/ipa_wdi3.h> #include <linux/msm_ipa.h> #include <linux/string.h> #include "../ipa_common_i.h" #define OFFLOAD_DRV_NAME "ipa_wdi3" #define IPA_WDI3_DBG(fmt, args...) \ do { \ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \ __func__, __LINE__, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) #define IPA_WDI3_LOW(fmt, args...) \ do { \ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \ __func__, __LINE__, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) #define IPA_WDI3_ERR(fmt, args...) \ do { \ pr_err(OFFLOAD_DRV_NAME " %s:%d " fmt, \ __func__, __LINE__, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) struct ipa_wdi3_intf_info { char netdev_name[IPA_RESOURCE_NAME_MAX]; u8 hdr_len; u32 partial_hdr_hdl[IPA_IP_MAX]; struct list_head link; }; struct ipa_wdi3_context { struct list_head head_intf_list; ipa_notify_cb notify; void *priv; struct completion wdi3_completion; struct mutex lock; }; static struct ipa_wdi3_context *ipa_wdi3_ctx; static int ipa_wdi3_commit_partial_hdr( struct ipa_ioc_add_hdr *hdr, const char *netdev_name, struct ipa_wdi3_hdr_info *hdr_info) { int i; if (!hdr || !hdr_info || !netdev_name) { IPA_WDI3_ERR("Invalid input\n"); return -EINVAL; } hdr->commit = 1; hdr->num_hdrs = 2; snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name), "%s_ipv4", netdev_name); snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name), "%s_ipv6", netdev_name); for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) { hdr->hdr[i].hdr_len = hdr_info[i].hdr_len; memcpy(hdr->hdr[i].hdr, hdr_info[i].hdr, hdr->hdr[i].hdr_len); hdr->hdr[i].type = hdr_info[i].hdr_type; hdr->hdr[i].is_partial = 1; hdr->hdr[i].is_eth2_ofst_valid = 1; hdr->hdr[i].eth2_ofst = hdr_info[i].dst_mac_addr_offset; } if (ipa_add_hdr(hdr)) { IPA_WDI3_ERR("fail to add partial headers\n"); return -EFAULT; } return 0; } int ipa_wdi3_reg_intf(struct ipa_wdi3_reg_intf_in_params *in) { struct ipa_ioc_add_hdr *hdr; struct ipa_wdi3_intf_info *new_intf; struct ipa_wdi3_intf_info *entry; struct ipa_tx_intf tx; struct ipa_rx_intf rx; struct ipa_ioc_tx_intf_prop tx_prop[2]; struct ipa_ioc_rx_intf_prop rx_prop[2]; u32 len; int ret = 0; if (in == NULL) { IPA_WDI3_ERR("invalid params in=%pK\n", in); return -EINVAL; } if (!ipa_wdi3_ctx) { ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL); if (ipa_wdi3_ctx == NULL) { IPA_WDI3_ERR("fail to alloc wdi3 ctx\n"); return -EFAULT; } mutex_init(&ipa_wdi3_ctx->lock); INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list); } IPA_WDI3_DBG("register interface for netdev %s\n", in->netdev_name); mutex_lock(&ipa_wdi3_ctx->lock); list_for_each_entry(entry, &ipa_wdi3_ctx->head_intf_list, link) if (strcmp(entry->netdev_name, in->netdev_name) == 0) { IPA_WDI3_DBG("intf was added before.\n"); mutex_unlock(&ipa_wdi3_ctx->lock); return 0; } IPA_WDI3_DBG("intf was not added before, proceed.\n"); new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL); if (new_intf == NULL) { IPA_WDI3_ERR("fail to alloc new intf\n"); mutex_unlock(&ipa_wdi3_ctx->lock); return -EFAULT; } INIT_LIST_HEAD(&new_intf->link); strlcpy(new_intf->netdev_name, in->netdev_name, sizeof(new_intf->netdev_name)); new_intf->hdr_len = in->hdr_info[0].hdr_len; /* add partial header */ len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_WDI3_ERR("fail to alloc %d bytes\n", len); ret = -EFAULT; goto fail_alloc_hdr; } if (ipa_wdi3_commit_partial_hdr(hdr, in->netdev_name, in->hdr_info)) { IPA_WDI3_ERR("fail to commit partial headers\n"); ret = -EFAULT; goto fail_commit_hdr; } new_intf->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl; new_intf->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl; IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", hdr->hdr[IPA_IP_v4].hdr_hdl, hdr->hdr[IPA_IP_v6].hdr_hdl); /* populate tx prop */ tx.num_props = 2; tx.prop = tx_prop; memset(tx_prop, 0, sizeof(tx_prop)); tx_prop[0].ip = IPA_IP_v4; tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS; tx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type; strlcpy(tx_prop[0].hdr_name, hdr->hdr[IPA_IP_v4].name, sizeof(tx_prop[0].hdr_name)); tx_prop[1].ip = IPA_IP_v6; tx_prop[1].dst_pipe = IPA_CLIENT_WLAN1_CONS; tx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type; strlcpy(tx_prop[1].hdr_name, hdr->hdr[IPA_IP_v6].name, sizeof(tx_prop[1].hdr_name)); /* populate rx prop */ rx.num_props = 2; rx.prop = rx_prop; memset(rx_prop, 0, sizeof(rx_prop)); rx_prop[0].ip = IPA_IP_v4; rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD; rx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type; if (in->is_meta_data_valid) { rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[0].attrib.meta_data = in->meta_data; rx_prop[0].attrib.meta_data_mask = in->meta_data_mask; } rx_prop[1].ip = IPA_IP_v6; rx_prop[1].src_pipe = IPA_CLIENT_WLAN1_PROD; rx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type; if (in->is_meta_data_valid) { rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[1].attrib.meta_data = in->meta_data; rx_prop[1].attrib.meta_data_mask = in->meta_data_mask; } if (ipa_register_intf(in->netdev_name, &tx, &rx)) { IPA_WDI3_ERR("fail to add interface prop\n"); ret = -EFAULT; goto fail_commit_hdr; } list_add(&new_intf->link, &ipa_wdi3_ctx->head_intf_list); init_completion(&ipa_wdi3_ctx->wdi3_completion); kfree(hdr); mutex_unlock(&ipa_wdi3_ctx->lock); return 0; fail_commit_hdr: kfree(hdr); fail_alloc_hdr: kfree(new_intf); mutex_unlock(&ipa_wdi3_ctx->lock); return ret; } EXPORT_SYMBOL(ipa_wdi3_reg_intf); int ipa_wdi3_dereg_intf(const char *netdev_name) { int len, ret = 0; struct ipa_ioc_del_hdr *hdr = NULL; struct ipa_wdi3_intf_info *entry; struct ipa_wdi3_intf_info *next; if (!netdev_name) { IPA_WDI3_ERR("no netdev name.\n"); return -EINVAL; } if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized.\n"); return -EPERM; } mutex_lock(&ipa_wdi3_ctx->lock); list_for_each_entry_safe(entry, next, &ipa_wdi3_ctx->head_intf_list, link) if (strcmp(entry->netdev_name, netdev_name) == 0) { len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_WDI3_ERR("fail to alloc %d bytes\n", len); mutex_unlock(&ipa_wdi3_ctx->lock); return -ENOMEM; } hdr->commit = 1; hdr->num_hdls = 2; hdr->hdl[0].hdl = entry->partial_hdr_hdl[0]; hdr->hdl[1].hdl = entry->partial_hdr_hdl[1]; IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n", hdr->hdl[0].hdl, hdr->hdl[1].hdl); if (ipa_del_hdr(hdr)) { IPA_WDI3_ERR("fail to delete partial header\n"); ret = -EFAULT; goto fail; } if (ipa_deregister_intf(entry->netdev_name)) { IPA_WDI3_ERR("fail to del interface props\n"); ret = -EFAULT; goto fail; } list_del(&entry->link); kfree(entry); break; } fail: kfree(hdr); mutex_unlock(&ipa_wdi3_ctx->lock); return ret; } EXPORT_SYMBOL(ipa_wdi3_dereg_intf); static void ipa_wdi3_rm_notify(void *user_data, enum ipa_rm_event event, unsigned long data) { if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("Invalid context\n"); return; } switch (event) { case IPA_RM_RESOURCE_GRANTED: complete_all(&ipa_wdi3_ctx->wdi3_completion); break; case IPA_RM_RESOURCE_RELEASED: break; default: IPA_WDI3_ERR("Invalid RM Evt: %d", event); break; } } static int ipa_wdi3_cons_release(void) { return 0; } static int ipa_wdi3_cons_request(void) { int ret = 0; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized\n"); ret = -EFAULT; } return ret; } int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out) { int ret = 0; struct ipa_rm_create_params param; if (!(in && out)) { IPA_WDI3_ERR("empty parameters. in=%pK out=%pK\n", in, out); return -EINVAL; } if (!ipa_wdi3_ctx) { ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL); if (ipa_wdi3_ctx == NULL) { IPA_WDI3_ERR("fail to alloc wdi3 ctx\n"); return -EFAULT; } mutex_init(&ipa_wdi3_ctx->lock); INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list); } ipa_wdi3_ctx->notify = in->notify; ipa_wdi3_ctx->priv = in->priv; memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_WLAN_PROD; param.reg_params.user_data = ipa_wdi3_ctx; param.reg_params.notify_cb = ipa_wdi3_rm_notify; param.floor_voltage = IPA_VOLTAGE_SVS; ret = ipa_rm_create_resource(¶m); if (ret) { IPA_WDI3_ERR("fail to create WLAN_PROD resource\n"); return -EFAULT; } memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_WLAN_CONS; param.request_resource = ipa_wdi3_cons_request; param.release_resource = ipa_wdi3_cons_release; ret = ipa_rm_create_resource(¶m); if (ret) { IPA_WDI3_ERR("fail to create WLAN_CONS resource\n"); goto fail_create_rm_cons; } if (ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_WDI3_ERR("fail to add rm dependency\n"); ret = -EFAULT; goto fail; } if (ipa_conn_wdi3_pipes(in, out)) { IPA_WDI3_ERR("fail to setup wdi3 pipes\n"); ret = -EFAULT; goto fail; } return 0; fail: ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS); fail_create_rm_cons: ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD); return ret; } EXPORT_SYMBOL(ipa_wdi3_conn_pipes); int ipa_wdi3_disconn_pipes(void) { int ipa_ep_idx_rx, ipa_ep_idx_tx; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized\n"); return -EPERM; } ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); if (ipa_disconn_wdi3_pipes(ipa_ep_idx_rx, ipa_ep_idx_tx)) { IPA_WDI3_ERR("fail to tear down wdi3 pipes\n"); return -EFAULT; } if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_WLAN_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_WDI3_ERR("fail to delete rm dependency\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD)) { IPA_WDI3_ERR("fail to delete WLAN_PROD resource\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS)) { IPA_WDI3_ERR("fail to delete WLAN_CONS resource\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_disconn_pipes); int ipa_wdi3_enable_pipes(void) { int ret; int ipa_ep_idx_tx, ipa_ep_idx_rx; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized.\n"); return -EPERM; } ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); if (ipa_enable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) { IPA_WDI3_ERR("fail to enable wdi3 pipes\n"); return -EFAULT; } ret = ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD); if (ret == -EINPROGRESS) { if (wait_for_completion_timeout(&ipa_wdi3_ctx->wdi3_completion, 10*HZ) == 0) { IPA_WDI3_ERR("WLAN_PROD resource req time out\n"); return -EFAULT; } } else if (ret != 0) { IPA_WDI3_ERR("fail to request resource\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_enable_pipes); int ipa_wdi3_disable_pipes(void) { int ret; int ipa_ep_idx_tx, ipa_ep_idx_rx; if (!ipa_wdi3_ctx) { IPA_WDI3_ERR("wdi3 ctx is not initialized.\n"); return -EPERM; } ret = ipa_rm_release_resource(IPA_RM_RESOURCE_WLAN_PROD); if (ret != 0) { IPA_WDI3_ERR("fail to release resource\n"); return -EFAULT; } ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS); if (ipa_disable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) { IPA_WDI3_ERR("fail to disable wdi3 pipes\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_disable_pipes); int ipa_wdi3_set_perf_profile(struct ipa_wdi3_perf_profile *profile) { struct ipa_rm_perf_profile rm_profile; enum ipa_rm_resource_name resource_name; if (profile == NULL) { IPA_WDI3_ERR("Invalid input\n"); return -EINVAL; } rm_profile.max_supported_bandwidth_mbps = profile->max_supported_bw_mbps; if (profile->client == IPA_CLIENT_WLAN1_PROD) { resource_name = IPA_RM_RESOURCE_WLAN_PROD; } else if (profile->client == IPA_CLIENT_WLAN1_CONS) { resource_name = IPA_RM_RESOURCE_WLAN_CONS; } else { IPA_WDI3_ERR("not supported\n"); return -EINVAL; } if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) { IPA_WDI3_ERR("fail to setup rm perf profile\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_wdi3_set_perf_profile);
drivers/platform/msm/ipa/ipa_common_i.h +11 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/ipc_logging.h> #include <linux/ipa.h> #include <linux/ipa_uc_offload.h> #include <linux/ipa_wdi3.h> #define __FILENAME__ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) Loading Loading @@ -375,6 +376,16 @@ int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); int ipa_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *user_data), void *user_data); void ipa_ntn_uc_dereg_rdyCB(void); int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, struct ipa_wdi3_conn_out_params *out); int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); const char *ipa_get_version_string(enum ipa_hw_type ver); int ipa_start_gsi_channel(u32 clnt_hdl); Loading