Loading drivers/platform/msm/ipa/ipa_api.c +30 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/ipa_uc_offload.h> #include "ipa_api.h" #define DRV_NAME "ipa" Loading Loading @@ -2672,6 +2673,35 @@ void ipa_assert(void) BUG(); } /** * ipa_setup_uc_ntn_pipes() - setup uc offload pipes */ int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *inp, ipa_notify_cb notify, void *priv, u8 hdr_len, struct ipa_ntn_conn_out_params *outp) { int ret; IPA_API_DISPATCH_RETURN(ipa_setup_uc_ntn_pipes, inp, notify, priv, hdr_len, outp); return ret; } /** * ipa_tear_down_uc_offload_pipes() - tear down uc offload pipes */ int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl) { int ret; IPA_API_DISPATCH_RETURN(ipa_tear_down_uc_offload_pipes, ipa_ep_idx_ul, ipa_ep_idx_dl); return ret; } static const struct dev_pm_ops ipa_pm_ops = { .suspend_noirq = ipa_ap_suspend, .resume_noirq = ipa_ap_resume, Loading drivers/platform/msm/ipa/ipa_api.h +7 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ */ #include <linux/ipa_mhi.h> #include <linux/ipa_uc_offload.h> #include "ipa_common_i.h" #ifndef _IPA_API_H_ Loading Loading @@ -358,6 +359,12 @@ struct ipa_api_controller { void *(*ipa_get_ipc_logbuf_low)(void); int (*ipa_setup_uc_ntn_pipes)(struct ipa_ntn_conn_in_params *in, ipa_notify_cb notify, void *priv, u8 hdr_len, struct ipa_ntn_conn_out_params *); int (*ipa_tear_down_uc_offload_pipes)(int ipa_ep_idx_ul, int ipa_ep_idx_dl); }; #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 obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o No newline at end of file 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 drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c 0 → 100644 +597 −0 Original line number Diff line number Diff line /* Copyright (c) 2015, 2016 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_uc_offload.h> #include <linux/msm_ipa.h> #include "../ipa_common_i.h" #define IPA_NTN_DMA_POOL_ALIGNMENT 8 #define OFFLOAD_DRV_NAME "ipa_uc_offload" #define IPA_UC_OFFLOAD_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_UC_OFFLOAD_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_UC_OFFLOAD_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) #define IPA_UC_OFFLOAD_INFO(fmt, args...) \ do { \ pr_info(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) enum ipa_uc_offload_state { IPA_UC_OFFLOAD_STATE_INVALID, IPA_UC_OFFLOAD_STATE_INITIALIZED, IPA_UC_OFFLOAD_STATE_UP, IPA_UC_OFFLOAD_STATE_DOWN, }; struct ipa_uc_offload_ctx { enum ipa_uc_offload_proto proto; enum ipa_uc_offload_state state; void *priv; u8 hdr_len; u32 partial_hdr_hdl[IPA_IP_MAX]; char netdev_name[IPA_RESOURCE_NAME_MAX]; ipa_notify_cb notify; struct completion ntn_completion; }; static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE]; static int ipa_commit_partial_hdr( struct ipa_ioc_add_hdr *hdr, const char *netdev_name, struct ipa_hdr_info *hdr_info) { int i; if (hdr == NULL || hdr_info == NULL) { IPA_UC_OFFLOAD_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_UC_OFFLOAD_ERR("fail to add partial headers\n"); return -EFAULT; } return 0; } static int ipa_uc_offload_ntn_reg_intf( struct ipa_uc_offload_intf_params *inp, struct ipa_uc_offload_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { struct ipa_ioc_add_hdr *hdr; 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; IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n", inp->netdev_name); memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX); ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len; ntn_ctx->notify = inp->notify; ntn_ctx->priv = inp->priv; /* 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_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len); return -ENOMEM; } if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) { IPA_UC_OFFLOAD_ERR("fail to commit partial headers\n"); ret = -EFAULT; goto fail; } /* 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_ODU_TETH_CONS; tx_prop[0].hdr_l2_type = inp->hdr_info[0].hdr_type; memcpy(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_ODU_TETH_CONS; tx_prop[1].hdr_l2_type = inp->hdr_info[1].hdr_type; memcpy(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_ODU_PROD; rx_prop[0].hdr_l2_type = inp->hdr_info[0].hdr_type; if (inp->is_meta_data_valid) { rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[0].attrib.meta_data = inp->meta_data; rx_prop[0].attrib.meta_data_mask = inp->meta_data_mask; } rx_prop[1].ip = IPA_IP_v6; rx_prop[1].src_pipe = IPA_CLIENT_ODU_PROD; rx_prop[1].hdr_l2_type = inp->hdr_info[1].hdr_type; if (inp->is_meta_data_valid) { rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[1].attrib.meta_data = inp->meta_data; rx_prop[1].attrib.meta_data_mask = inp->meta_data_mask; } if (ipa_register_intf(inp->netdev_name, &tx, &rx)) { IPA_UC_OFFLOAD_ERR("fail to add interface prop\n"); memset(ntn_ctx, 0, sizeof(*ntn_ctx)); ret = -EFAULT; goto fail; } ntn_ctx->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl; ntn_ctx->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl; init_completion(&ntn_ctx->ntn_completion); ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED; fail: kfree(hdr); return ret; } int ipa_uc_offload_reg_intf( struct ipa_uc_offload_intf_params *inp, struct ipa_uc_offload_out_params *outp) { struct ipa_uc_offload_ctx *ctx; int ret = 0; if (inp == NULL || outp == NULL) { IPA_UC_OFFLOAD_ERR("invalid params in=%p out=%p\n", inp, outp); return -EINVAL; } if (inp->proto <= IPA_UC_INVALID || inp->proto >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("invalid proto %d\n", inp->proto); return -EINVAL; } if (!ipa_uc_offload_ctx[inp->proto]) { ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (ctx == NULL) { IPA_UC_OFFLOAD_ERR("fail to alloc uc offload ctx\n"); return -EFAULT; } ipa_uc_offload_ctx[inp->proto] = ctx; ctx->proto = inp->proto; } else ctx = ipa_uc_offload_ctx[inp->proto]; if (ctx->state != IPA_UC_OFFLOAD_STATE_INVALID) { IPA_UC_OFFLOAD_ERR("Already Initialized\n"); return -EINVAL; } if (ctx->proto == IPA_UC_NTN) { ret = ipa_uc_offload_ntn_reg_intf(inp, outp, ctx); if (!ret) outp->clnt_hndl = IPA_UC_NTN; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_reg_intf); static int ipa_uc_ntn_cons_release(void) { return 0; } static int ipa_uc_ntn_cons_request(void) { int ret = 0; struct ipa_uc_offload_ctx *ntn_ctx; ntn_ctx = ipa_uc_offload_ctx[IPA_UC_NTN]; if (!ntn_ctx) { IPA_UC_OFFLOAD_ERR("NTN is not initialized\n"); ret = -EFAULT; } else if (ntn_ctx->state != IPA_UC_OFFLOAD_STATE_UP) { IPA_UC_OFFLOAD_ERR("Invalid State: %d\n", ntn_ctx->state); ret = -EFAULT; } return ret; } static void ipa_uc_offload_rm_notify(void *user_data, enum ipa_rm_event event, unsigned long data) { struct ipa_uc_offload_ctx *offload_ctx; offload_ctx = (struct ipa_uc_offload_ctx *)user_data; if (!(offload_ctx && offload_ctx->proto > IPA_UC_INVALID && offload_ctx->proto < IPA_UC_MAX_PROT_SIZE)) { IPA_UC_OFFLOAD_ERR("Invalid user data\n"); return; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) IPA_UC_OFFLOAD_ERR("Invalid State: %d\n", offload_ctx->state); switch (event) { case IPA_RM_RESOURCE_GRANTED: complete_all(&offload_ctx->ntn_completion); break; case IPA_RM_RESOURCE_RELEASED: break; default: IPA_UC_OFFLOAD_ERR("Invalid RM Evt: %d", event); break; } } int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, struct ipa_ntn_conn_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { struct ipa_rm_create_params param; int result = 0; if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT || inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) { IPA_UC_OFFLOAD_ERR("alignment failure on TX\n"); return -EINVAL; } if (inp->ul.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT || inp->ul.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) { IPA_UC_OFFLOAD_ERR("alignment failure on RX\n"); return -EINVAL; } memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; param.reg_params.user_data = ntn_ctx; param.reg_params.notify_cb = ipa_uc_offload_rm_notify; param.floor_voltage = IPA_VOLTAGE_SVS; result = ipa_rm_create_resource(¶m); if (result) { IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n"); return -EFAULT; } memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; param.request_resource = ipa_uc_ntn_cons_request; param.release_resource = ipa_uc_ntn_cons_release; result = ipa_rm_create_resource(¶m); if (result) { IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n"); goto fail_create_rm_cons; } if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n"); result = -EFAULT; goto fail; } if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify, ntn_ctx->priv, ntn_ctx->hdr_len, outp)) { IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n"); result = -EFAULT; goto fail; } ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP; result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); if (result == -EINPROGRESS) { if (wait_for_completion_timeout(&ntn_ctx->ntn_completion, 10*HZ) == 0) { IPA_UC_OFFLOAD_ERR("ODU PROD resource req time out\n"); result = -EFAULT; goto fail; } } else if (result != 0) { IPA_UC_OFFLOAD_ERR("fail to request resource\n"); result = -EFAULT; goto fail; } return 0; fail: ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); fail_create_rm_cons: ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); return result; } int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp, struct ipa_uc_offload_conn_out_params *outp) { int ret = 0; struct ipa_uc_offload_ctx *offload_ctx; if (!(inp && outp)) { IPA_UC_OFFLOAD_ERR("bad parm. in=%p out=%p\n", inp, outp); return -EINVAL; } if (inp->clnt_hndl <= IPA_UC_INVALID || inp->clnt_hndl >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("invalid client handle %d\n", inp->clnt_hndl); return -EINVAL; } offload_ctx = ipa_uc_offload_ctx[inp->clnt_hndl]; if (!offload_ctx) { IPA_UC_OFFLOAD_ERR("Invalid Handle\n"); return -EINVAL; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) { IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state); return -EPERM; } switch (offload_ctx->proto) { case IPA_UC_NTN: ret = ipa_uc_ntn_conn_pipes(&inp->u.ntn, &outp->u.ntn, offload_ctx); break; default: IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", offload_ctx->proto); ret = -EINVAL; break; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_conn_pipes); int ipa_set_perf_profile(struct ipa_perf_profile *profile) { struct ipa_rm_perf_profile rm_profile; enum ipa_rm_resource_name resource_name; if (profile == NULL) { IPA_UC_OFFLOAD_ERR("Invalid input\n"); return -EINVAL; } rm_profile.max_supported_bandwidth_mbps = profile->max_supported_bw_mbps; if (profile->client == IPA_CLIENT_ODU_PROD) { resource_name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; } else if (profile->client == IPA_CLIENT_ODU_TETH_CONS) { resource_name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; } else { IPA_UC_OFFLOAD_ERR("not supported\n"); return -EINVAL; } if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) { IPA_UC_OFFLOAD_ERR("fail to setup rm perf profile\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_set_perf_profile); static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx) { int ipa_ep_idx_ul, ipa_ep_idx_dl; ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN; if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) { IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) { IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n"); return -EFAULT; } ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD); ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS); if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) { IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n"); return -EFAULT; } return 0; } int ipa_uc_offload_disconn_pipes(u32 clnt_hdl) { struct ipa_uc_offload_ctx *offload_ctx; int ret = 0; if (clnt_hdl <= IPA_UC_INVALID || clnt_hdl >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl); return -EINVAL; } offload_ctx = ipa_uc_offload_ctx[clnt_hdl]; if (!offload_ctx) { IPA_UC_OFFLOAD_ERR("Invalid client Handle\n"); return -EINVAL; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_UP) { IPA_UC_OFFLOAD_ERR("Invalid state\n"); return -EINVAL; } switch (offload_ctx->proto) { case IPA_UC_NTN: ret = ipa_uc_ntn_disconn_pipes(offload_ctx); break; default: IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", clnt_hdl); ret = -EINVAL; break; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_disconn_pipes); static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx) { int len, result = 0; struct ipa_ioc_del_hdr *hdr; len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len); return -ENOMEM; } hdr->commit = 1; hdr->num_hdls = 2; hdr->hdl[0].hdl = ntn_ctx->partial_hdr_hdl[0]; hdr->hdl[1].hdl = ntn_ctx->partial_hdr_hdl[1]; if (ipa_del_hdr(hdr)) { IPA_UC_OFFLOAD_ERR("fail to delete partial header\n"); result = -EFAULT; goto fail; } if (ipa_deregister_intf(ntn_ctx->netdev_name)) { IPA_UC_OFFLOAD_ERR("fail to delete interface prop\n"); result = -EFAULT; goto fail; } fail: kfree(hdr); return result; } int ipa_uc_offload_cleanup(u32 clnt_hdl) { struct ipa_uc_offload_ctx *offload_ctx; int ret = 0; if (clnt_hdl <= IPA_UC_INVALID || clnt_hdl >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl); return -EINVAL; } offload_ctx = ipa_uc_offload_ctx[clnt_hdl]; if (!offload_ctx) { IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl); return -EINVAL; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) { IPA_UC_OFFLOAD_ERR("Invalid State %d\n", offload_ctx->state); return -EINVAL; } switch (offload_ctx->proto) { case IPA_UC_NTN: ret = ipa_uc_ntn_cleanup(offload_ctx); break; default: IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", clnt_hdl); ret = -EINVAL; break; } if (!ret) { kfree(offload_ctx); offload_ctx = NULL; ipa_uc_offload_ctx[clnt_hdl] = NULL; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_cleanup); drivers/platform/msm/ipa/ipa_common_i.h +6 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #define _IPA_COMMON_I_H_ #include <linux/ipc_logging.h> #include <linux/ipa.h> #include <linux/ipa_uc_offload.h> #define __FILENAME__ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) Loading Loading @@ -342,5 +343,10 @@ int ipa_uc_state_check(void); void ipa_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb); void ipa_set_tag_process_before_gating(bool val); bool ipa_has_open_aggr_frame(enum ipa_client_type client); int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, ipa_notify_cb notify, void *priv, u8 hdr_len, struct ipa_ntn_conn_out_params *outp); int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); #endif /* _IPA_COMMON_I_H_ */ Loading
drivers/platform/msm/ipa/ipa_api.c +30 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/ipa_uc_offload.h> #include "ipa_api.h" #define DRV_NAME "ipa" Loading Loading @@ -2672,6 +2673,35 @@ void ipa_assert(void) BUG(); } /** * ipa_setup_uc_ntn_pipes() - setup uc offload pipes */ int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *inp, ipa_notify_cb notify, void *priv, u8 hdr_len, struct ipa_ntn_conn_out_params *outp) { int ret; IPA_API_DISPATCH_RETURN(ipa_setup_uc_ntn_pipes, inp, notify, priv, hdr_len, outp); return ret; } /** * ipa_tear_down_uc_offload_pipes() - tear down uc offload pipes */ int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl) { int ret; IPA_API_DISPATCH_RETURN(ipa_tear_down_uc_offload_pipes, ipa_ep_idx_ul, ipa_ep_idx_dl); return ret; } static const struct dev_pm_ops ipa_pm_ops = { .suspend_noirq = ipa_ap_suspend, .resume_noirq = ipa_ap_resume, Loading
drivers/platform/msm/ipa/ipa_api.h +7 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ */ #include <linux/ipa_mhi.h> #include <linux/ipa_uc_offload.h> #include "ipa_common_i.h" #ifndef _IPA_API_H_ Loading Loading @@ -358,6 +359,12 @@ struct ipa_api_controller { void *(*ipa_get_ipc_logbuf_low)(void); int (*ipa_setup_uc_ntn_pipes)(struct ipa_ntn_conn_in_params *in, ipa_notify_cb notify, void *priv, u8 hdr_len, struct ipa_ntn_conn_out_params *); int (*ipa_tear_down_uc_offload_pipes)(int ipa_ep_idx_ul, int ipa_ep_idx_dl); }; #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 obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o No newline at end of file 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
drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c 0 → 100644 +597 −0 Original line number Diff line number Diff line /* Copyright (c) 2015, 2016 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_uc_offload.h> #include <linux/msm_ipa.h> #include "../ipa_common_i.h" #define IPA_NTN_DMA_POOL_ALIGNMENT 8 #define OFFLOAD_DRV_NAME "ipa_uc_offload" #define IPA_UC_OFFLOAD_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_UC_OFFLOAD_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_UC_OFFLOAD_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) #define IPA_UC_OFFLOAD_INFO(fmt, args...) \ do { \ pr_info(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) enum ipa_uc_offload_state { IPA_UC_OFFLOAD_STATE_INVALID, IPA_UC_OFFLOAD_STATE_INITIALIZED, IPA_UC_OFFLOAD_STATE_UP, IPA_UC_OFFLOAD_STATE_DOWN, }; struct ipa_uc_offload_ctx { enum ipa_uc_offload_proto proto; enum ipa_uc_offload_state state; void *priv; u8 hdr_len; u32 partial_hdr_hdl[IPA_IP_MAX]; char netdev_name[IPA_RESOURCE_NAME_MAX]; ipa_notify_cb notify; struct completion ntn_completion; }; static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE]; static int ipa_commit_partial_hdr( struct ipa_ioc_add_hdr *hdr, const char *netdev_name, struct ipa_hdr_info *hdr_info) { int i; if (hdr == NULL || hdr_info == NULL) { IPA_UC_OFFLOAD_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_UC_OFFLOAD_ERR("fail to add partial headers\n"); return -EFAULT; } return 0; } static int ipa_uc_offload_ntn_reg_intf( struct ipa_uc_offload_intf_params *inp, struct ipa_uc_offload_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { struct ipa_ioc_add_hdr *hdr; 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; IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n", inp->netdev_name); memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX); ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len; ntn_ctx->notify = inp->notify; ntn_ctx->priv = inp->priv; /* 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_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len); return -ENOMEM; } if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) { IPA_UC_OFFLOAD_ERR("fail to commit partial headers\n"); ret = -EFAULT; goto fail; } /* 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_ODU_TETH_CONS; tx_prop[0].hdr_l2_type = inp->hdr_info[0].hdr_type; memcpy(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_ODU_TETH_CONS; tx_prop[1].hdr_l2_type = inp->hdr_info[1].hdr_type; memcpy(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_ODU_PROD; rx_prop[0].hdr_l2_type = inp->hdr_info[0].hdr_type; if (inp->is_meta_data_valid) { rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[0].attrib.meta_data = inp->meta_data; rx_prop[0].attrib.meta_data_mask = inp->meta_data_mask; } rx_prop[1].ip = IPA_IP_v6; rx_prop[1].src_pipe = IPA_CLIENT_ODU_PROD; rx_prop[1].hdr_l2_type = inp->hdr_info[1].hdr_type; if (inp->is_meta_data_valid) { rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA; rx_prop[1].attrib.meta_data = inp->meta_data; rx_prop[1].attrib.meta_data_mask = inp->meta_data_mask; } if (ipa_register_intf(inp->netdev_name, &tx, &rx)) { IPA_UC_OFFLOAD_ERR("fail to add interface prop\n"); memset(ntn_ctx, 0, sizeof(*ntn_ctx)); ret = -EFAULT; goto fail; } ntn_ctx->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl; ntn_ctx->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl; init_completion(&ntn_ctx->ntn_completion); ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED; fail: kfree(hdr); return ret; } int ipa_uc_offload_reg_intf( struct ipa_uc_offload_intf_params *inp, struct ipa_uc_offload_out_params *outp) { struct ipa_uc_offload_ctx *ctx; int ret = 0; if (inp == NULL || outp == NULL) { IPA_UC_OFFLOAD_ERR("invalid params in=%p out=%p\n", inp, outp); return -EINVAL; } if (inp->proto <= IPA_UC_INVALID || inp->proto >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("invalid proto %d\n", inp->proto); return -EINVAL; } if (!ipa_uc_offload_ctx[inp->proto]) { ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (ctx == NULL) { IPA_UC_OFFLOAD_ERR("fail to alloc uc offload ctx\n"); return -EFAULT; } ipa_uc_offload_ctx[inp->proto] = ctx; ctx->proto = inp->proto; } else ctx = ipa_uc_offload_ctx[inp->proto]; if (ctx->state != IPA_UC_OFFLOAD_STATE_INVALID) { IPA_UC_OFFLOAD_ERR("Already Initialized\n"); return -EINVAL; } if (ctx->proto == IPA_UC_NTN) { ret = ipa_uc_offload_ntn_reg_intf(inp, outp, ctx); if (!ret) outp->clnt_hndl = IPA_UC_NTN; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_reg_intf); static int ipa_uc_ntn_cons_release(void) { return 0; } static int ipa_uc_ntn_cons_request(void) { int ret = 0; struct ipa_uc_offload_ctx *ntn_ctx; ntn_ctx = ipa_uc_offload_ctx[IPA_UC_NTN]; if (!ntn_ctx) { IPA_UC_OFFLOAD_ERR("NTN is not initialized\n"); ret = -EFAULT; } else if (ntn_ctx->state != IPA_UC_OFFLOAD_STATE_UP) { IPA_UC_OFFLOAD_ERR("Invalid State: %d\n", ntn_ctx->state); ret = -EFAULT; } return ret; } static void ipa_uc_offload_rm_notify(void *user_data, enum ipa_rm_event event, unsigned long data) { struct ipa_uc_offload_ctx *offload_ctx; offload_ctx = (struct ipa_uc_offload_ctx *)user_data; if (!(offload_ctx && offload_ctx->proto > IPA_UC_INVALID && offload_ctx->proto < IPA_UC_MAX_PROT_SIZE)) { IPA_UC_OFFLOAD_ERR("Invalid user data\n"); return; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) IPA_UC_OFFLOAD_ERR("Invalid State: %d\n", offload_ctx->state); switch (event) { case IPA_RM_RESOURCE_GRANTED: complete_all(&offload_ctx->ntn_completion); break; case IPA_RM_RESOURCE_RELEASED: break; default: IPA_UC_OFFLOAD_ERR("Invalid RM Evt: %d", event); break; } } int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, struct ipa_ntn_conn_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { struct ipa_rm_create_params param; int result = 0; if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT || inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) { IPA_UC_OFFLOAD_ERR("alignment failure on TX\n"); return -EINVAL; } if (inp->ul.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT || inp->ul.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) { IPA_UC_OFFLOAD_ERR("alignment failure on RX\n"); return -EINVAL; } memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; param.reg_params.user_data = ntn_ctx; param.reg_params.notify_cb = ipa_uc_offload_rm_notify; param.floor_voltage = IPA_VOLTAGE_SVS; result = ipa_rm_create_resource(¶m); if (result) { IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n"); return -EFAULT; } memset(¶m, 0, sizeof(param)); param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; param.request_resource = ipa_uc_ntn_cons_request; param.release_resource = ipa_uc_ntn_cons_release; result = ipa_rm_create_resource(¶m); if (result) { IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n"); goto fail_create_rm_cons; } if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n"); result = -EFAULT; goto fail; } if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify, ntn_ctx->priv, ntn_ctx->hdr_len, outp)) { IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n"); result = -EFAULT; goto fail; } ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP; result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); if (result == -EINPROGRESS) { if (wait_for_completion_timeout(&ntn_ctx->ntn_completion, 10*HZ) == 0) { IPA_UC_OFFLOAD_ERR("ODU PROD resource req time out\n"); result = -EFAULT; goto fail; } } else if (result != 0) { IPA_UC_OFFLOAD_ERR("fail to request resource\n"); result = -EFAULT; goto fail; } return 0; fail: ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); fail_create_rm_cons: ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); return result; } int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp, struct ipa_uc_offload_conn_out_params *outp) { int ret = 0; struct ipa_uc_offload_ctx *offload_ctx; if (!(inp && outp)) { IPA_UC_OFFLOAD_ERR("bad parm. in=%p out=%p\n", inp, outp); return -EINVAL; } if (inp->clnt_hndl <= IPA_UC_INVALID || inp->clnt_hndl >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("invalid client handle %d\n", inp->clnt_hndl); return -EINVAL; } offload_ctx = ipa_uc_offload_ctx[inp->clnt_hndl]; if (!offload_ctx) { IPA_UC_OFFLOAD_ERR("Invalid Handle\n"); return -EINVAL; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) { IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state); return -EPERM; } switch (offload_ctx->proto) { case IPA_UC_NTN: ret = ipa_uc_ntn_conn_pipes(&inp->u.ntn, &outp->u.ntn, offload_ctx); break; default: IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", offload_ctx->proto); ret = -EINVAL; break; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_conn_pipes); int ipa_set_perf_profile(struct ipa_perf_profile *profile) { struct ipa_rm_perf_profile rm_profile; enum ipa_rm_resource_name resource_name; if (profile == NULL) { IPA_UC_OFFLOAD_ERR("Invalid input\n"); return -EINVAL; } rm_profile.max_supported_bandwidth_mbps = profile->max_supported_bw_mbps; if (profile->client == IPA_CLIENT_ODU_PROD) { resource_name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; } else if (profile->client == IPA_CLIENT_ODU_TETH_CONS) { resource_name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; } else { IPA_UC_OFFLOAD_ERR("not supported\n"); return -EINVAL; } if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) { IPA_UC_OFFLOAD_ERR("fail to setup rm perf profile\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(ipa_set_perf_profile); static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx) { int ipa_ep_idx_ul, ipa_ep_idx_dl; ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN; if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, IPA_RM_RESOURCE_APPS_CONS)) { IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) { IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n"); return -EFAULT; } if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) { IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n"); return -EFAULT; } ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD); ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS); if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) { IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n"); return -EFAULT; } return 0; } int ipa_uc_offload_disconn_pipes(u32 clnt_hdl) { struct ipa_uc_offload_ctx *offload_ctx; int ret = 0; if (clnt_hdl <= IPA_UC_INVALID || clnt_hdl >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl); return -EINVAL; } offload_ctx = ipa_uc_offload_ctx[clnt_hdl]; if (!offload_ctx) { IPA_UC_OFFLOAD_ERR("Invalid client Handle\n"); return -EINVAL; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_UP) { IPA_UC_OFFLOAD_ERR("Invalid state\n"); return -EINVAL; } switch (offload_ctx->proto) { case IPA_UC_NTN: ret = ipa_uc_ntn_disconn_pipes(offload_ctx); break; default: IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", clnt_hdl); ret = -EINVAL; break; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_disconn_pipes); static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx) { int len, result = 0; struct ipa_ioc_del_hdr *hdr; len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len); return -ENOMEM; } hdr->commit = 1; hdr->num_hdls = 2; hdr->hdl[0].hdl = ntn_ctx->partial_hdr_hdl[0]; hdr->hdl[1].hdl = ntn_ctx->partial_hdr_hdl[1]; if (ipa_del_hdr(hdr)) { IPA_UC_OFFLOAD_ERR("fail to delete partial header\n"); result = -EFAULT; goto fail; } if (ipa_deregister_intf(ntn_ctx->netdev_name)) { IPA_UC_OFFLOAD_ERR("fail to delete interface prop\n"); result = -EFAULT; goto fail; } fail: kfree(hdr); return result; } int ipa_uc_offload_cleanup(u32 clnt_hdl) { struct ipa_uc_offload_ctx *offload_ctx; int ret = 0; if (clnt_hdl <= IPA_UC_INVALID || clnt_hdl >= IPA_UC_MAX_PROT_SIZE) { IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl); return -EINVAL; } offload_ctx = ipa_uc_offload_ctx[clnt_hdl]; if (!offload_ctx) { IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl); return -EINVAL; } if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) { IPA_UC_OFFLOAD_ERR("Invalid State %d\n", offload_ctx->state); return -EINVAL; } switch (offload_ctx->proto) { case IPA_UC_NTN: ret = ipa_uc_ntn_cleanup(offload_ctx); break; default: IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", clnt_hdl); ret = -EINVAL; break; } if (!ret) { kfree(offload_ctx); offload_ctx = NULL; ipa_uc_offload_ctx[clnt_hdl] = NULL; } return ret; } EXPORT_SYMBOL(ipa_uc_offload_cleanup);
drivers/platform/msm/ipa/ipa_common_i.h +6 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #define _IPA_COMMON_I_H_ #include <linux/ipc_logging.h> #include <linux/ipa.h> #include <linux/ipa_uc_offload.h> #define __FILENAME__ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) Loading Loading @@ -342,5 +343,10 @@ int ipa_uc_state_check(void); void ipa_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb); void ipa_set_tag_process_before_gating(bool val); bool ipa_has_open_aggr_frame(enum ipa_client_type client); int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, ipa_notify_cb notify, void *priv, u8 hdr_len, struct ipa_ntn_conn_out_params *outp); int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); #endif /* _IPA_COMMON_I_H_ */