blob: dbbd104390763a388e8ad8307c1e8eb381d6baff [file] [log] [blame]
/*
* 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.
*/
/*=== includes ===*/
/* header files for OS primitives */
#include <osdep.h> /* uint32_t, etc. */
#include <qdf_mem.h> /* qdf_mem_malloc,free */
#include <qdf_types.h> /* qdf_device_t, qdf_print */
#include <qdf_lock.h> /* qdf_spinlock */
#include <qdf_atomic.h> /* qdf_atomic_read */
#if defined(HIF_PCI) || defined(HIF_SNOC) || defined(HIF_AHB)
/* Required for WLAN_FEATURE_FASTPATH */
#include <ce_api.h>
#endif
/* header files for utilities */
#include <cds_queue.h> /* TAILQ */
/* header files for configuration API */
#include <ol_cfg.h> /* ol_cfg_is_high_latency */
#include <ol_if_athvar.h>
/* header files for HTT API */
#include <ol_htt_api.h>
#include <ol_htt_tx_api.h>
/* header files for our own APIs */
#include <ol_txrx_api.h>
#include <ol_txrx_dbg.h>
#include <cdp_txrx_ocb.h>
#include <ol_txrx_ctrl_api.h>
#include <cdp_txrx_stats.h>
#include <ol_txrx_osif_api.h>
/* header files for our internal definitions */
#include <ol_txrx_internal.h> /* TXRX_ASSERT, etc. */
#include <wdi_event.h> /* WDI events */
#include <ol_tx.h> /* ol_tx_ll */
#include <ol_rx.h> /* ol_rx_deliver */
#include <ol_txrx_peer_find.h> /* ol_txrx_peer_find_attach, etc. */
#include <ol_rx_pn.h> /* ol_rx_pn_check, etc. */
#include <ol_rx_fwd.h> /* ol_rx_fwd_check, etc. */
#include <ol_rx_reorder_timeout.h> /* OL_RX_REORDER_TIMEOUT_INIT, etc. */
#include <ol_rx_reorder.h>
#include <ol_tx_send.h> /* ol_tx_discard_target_frms */
#include <ol_tx_desc.h> /* ol_tx_desc_frame_free */
#include <ol_tx_queue.h>
#include <ol_tx_sched.h> /* ol_tx_sched_attach, etc. */
#include <ol_txrx.h>
#include <ol_txrx_types.h>
#include <cdp_txrx_flow_ctrl_legacy.h>
#include <cdp_txrx_bus.h>
#include <cdp_txrx_ipa.h>
#include <cdp_txrx_pmf.h>
#include "wma.h"
#include "hif.h"
#include <cdp_txrx_peer_ops.h>
#ifndef REMOVE_PKT_LOG
#include "pktlog_ac.h"
#endif
#include "epping_main.h"
#include <a_types.h>
#ifdef IPA_OFFLOAD
#include <ol_txrx_ipa.h>
/* For Tx pipes, use Ethernet-II Header format */
struct ol_txrx_ipa_uc_tx_hdr ipa_uc_tx_hdr = {
{
0x0000,
0x00000000,
0x00000000
},
{
0x00000000
},
{
{0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc},
{0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff},
0x0008
}
};
/**
* ol_txrx_ipa_uc_get_resource() - Client request resource information
* @pdev: handle to the HTT instance
*
* OL client will reuqest IPA UC related resource information
* Resource information will be distributted to IPA module
* All of the required resources should be pre-allocated
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_uc_get_resource(struct cdp_pdev *ppdev)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
htt_ipa_uc_get_resource(pdev->htt_pdev,
&ipa_res->ce_sr_base_paddr,
&ipa_res->ce_sr_ring_size,
&ipa_res->ce_reg_paddr,
&ipa_res->tx_comp_ring_base_paddr,
&ipa_res->tx_comp_ring_size,
&ipa_res->tx_num_alloc_buffer,
&ipa_res->rx_rdy_ring_base_paddr,
&ipa_res->rx_rdy_ring_size,
&ipa_res->rx_proc_done_idx_paddr,
&ipa_res->rx_proc_done_idx_vaddr,
&ipa_res->rx2_rdy_ring_base_paddr,
&ipa_res->rx2_rdy_ring_size,
&ipa_res->rx2_proc_done_idx_paddr,
&ipa_res->rx2_proc_done_idx_vaddr);
if ((0 == ipa_res->ce_sr_base_paddr) ||
(0 == ipa_res->tx_comp_ring_base_paddr) ||
(0 == ipa_res->rx_rdy_ring_base_paddr)
#if defined(QCA_WIFI_3_0) && defined(CONFIG_IPA3)
|| (0 == ipa_res->rx2_rdy_ring_base_paddr)
#endif
)
return QDF_STATUS_E_FAILURE;
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_set_doorbell_paddr() - Client set IPA UC doorbell register
* @pdev: handle to the HTT instance
*
* IPA UC let know doorbell register physical address
* WLAN firmware will use this physical address to notify IPA UC
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_uc_set_doorbell_paddr(struct cdp_pdev *ppdev)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
int ret;
ret = htt_ipa_uc_set_doorbell_paddr(pdev->htt_pdev,
ipa_res->tx_comp_doorbell_paddr,
ipa_res->rx_ready_doorbell_paddr);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"htt_ipa_uc_set_doorbell_paddr fail: %d", ret);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_set_active() - Client notify IPA UC data path active or not
* @pdev: handle to the HTT instance
* @uc_active: WDI UC path enable or not
* @is_tx: TX path or RX path
*
* IPA UC let know doorbell register physical address
* WLAN firmware will use this physical address to notify IPA UC
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_uc_set_active(struct cdp_pdev *ppdev, bool uc_active,
bool is_tx)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
int ret;
ret = htt_h2t_ipa_uc_set_active(pdev->htt_pdev, uc_active, is_tx);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"htt_h2t_ipa_uc_set_active fail: %d", ret);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_op_response() - Handle OP command response from firmware
* @pdev: handle to the device instance
* @op_msg: op response message from firmware
*
* Return: none
*/
QDF_STATUS ol_txrx_ipa_uc_op_response(struct cdp_pdev *ppdev, uint8_t *op_msg)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
if (pdev->ipa_uc_op_cb) {
pdev->ipa_uc_op_cb(op_msg, pdev->usr_ctxt);
} else {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: IPA callback function is not registered", __func__);
qdf_mem_free(op_msg);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_register_op_cb() - Register OP handler function
* @pdev: handle to the device instance
* @op_cb: handler function pointer
*
* Return: none
*/
QDF_STATUS ol_txrx_ipa_uc_register_op_cb(struct cdp_pdev *ppdev,
ipa_uc_op_cb_type op_cb, void *usr_ctxt)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
pdev->ipa_uc_op_cb = op_cb;
pdev->usr_ctxt = usr_ctxt;
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_get_stat() - Get firmware wdi status
* @pdev: handle to the HTT instance
*
* Return: none
*/
QDF_STATUS ol_txrx_ipa_uc_get_stat(struct cdp_pdev *ppdev)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
int ret;
ret = htt_h2t_ipa_uc_get_stats(pdev->htt_pdev);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"htt_h2t_ipa_uc_get_stats fail: %d", ret);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_enable_autonomy() - Enable autonomy RX path
* @pdev: handle to the device instance
*
* Set all RX packet route to IPA
* Return: none
*/
QDF_STATUS ol_txrx_ipa_enable_autonomy(struct cdp_pdev *ppdev)
{
/* TBD */
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_disable_autonomy() - Disable autonomy RX path
* @pdev: handle to the device instance
*
* Disable RX packet route to host
* Return: none
*/
QDF_STATUS ol_txrx_ipa_disable_autonomy(struct cdp_pdev *ppdev)
{
/* TBD */
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_setup() - Setup and connect IPA pipes
* @pdev: handle to the device instance
* @ipa_i2w_cb: IPA to WLAN callback
* @ipa_w2i_cb: WLAN to IPA callback
* @ipa_wdi_meter_notifier_cb: IPA WDI metering callback
* @ipa_desc_size: IPA descriptor size
* @ipa_priv: handle to the HTT instance
* @is_rm_enabled: Is IPA RM enabled or not
* @p_tx_pipe_handle: pointer to Tx pipe handle
* @p_rx_pipe_handle: pointer to Rx pipe handle
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_setup(struct cdp_pdev *ppdev, void *ipa_i2w_cb,
void *ipa_w2i_cb, void *ipa_wdi_meter_notifier_cb,
uint32_t ipa_desc_size, void *ipa_priv,
bool is_rm_enabled, uint32_t *p_tx_pipe_handle,
uint32_t *p_rx_pipe_handle)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
struct ipa_wdi_in_params pipe_in;
struct ipa_wdi_out_params pipe_out;
int ret;
qdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params));
qdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params));
/* TX PIPE */
pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len =
OL_TXRX_IPA_UC_WLAN_8023_HDR_SIZE;
pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS;
pipe_in.sys.desc_fifo_sz = ipa_desc_size;
pipe_in.sys.priv = ipa_priv;
pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
pipe_in.sys.notify = ipa_i2w_cb;
if (!is_rm_enabled) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: IPA RM DISABLED, IPA AWAKE", __func__);
pipe_in.sys.keep_ipa_awake = true;
}
pipe_in.u.dl.comp_ring_base_pa = ipa_res->tx_comp_ring_base_paddr;
pipe_in.u.dl.comp_ring_size = ipa_res->tx_comp_ring_size *
sizeof(qdf_dma_addr_t);
pipe_in.u.dl.ce_ring_base_pa = ipa_res->ce_sr_base_paddr;
pipe_in.u.dl.ce_door_bell_pa = ipa_res->ce_reg_paddr;
pipe_in.u.dl.ce_ring_size = ipa_res->ce_sr_ring_size;
pipe_in.u.dl.num_tx_buffers = ipa_res->tx_num_alloc_buffer;
/* Connect WDI IPA PIPE */
ret = ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"ipa_connect_wdi_pipe: Tx pipe setup failed: ret=%d", ret);
return QDF_STATUS_E_FAILURE;
}
/* Micro Controller Doorbell register */
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s CONS DB pipe out 0x%x TX PIPE Handle 0x%x",
__func__, (unsigned int)pipe_out.uc_door_bell_pa,
pipe_out.clnt_hdl);
ipa_res->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa;
/* WLAN TX PIPE Handle */
ipa_res->tx_pipe_handle = pipe_out.clnt_hdl;
*p_tx_pipe_handle = pipe_out.clnt_hdl;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"TX: %s 0x%x, %s %d, %s 0x%x, %s 0x%x, %s %d, %sNB %d, %s 0x%x",
"comp_ring_base_pa",
(unsigned int)pipe_in.u.dl.comp_ring_base_pa,
"comp_ring_size",
pipe_in.u.dl.comp_ring_size,
"ce_ring_base_pa",
(unsigned int)pipe_in.u.dl.ce_ring_base_pa,
"ce_door_bell_pa",
(unsigned int)pipe_in.u.dl.ce_door_bell_pa,
"ce_ring_size",
pipe_in.u.dl.ce_ring_size,
"num_tx_buffers",
pipe_in.u.dl.num_tx_buffers,
"tx_comp_doorbell_paddr",
(unsigned int)ipa_res->tx_comp_doorbell_paddr);
/* RX PIPE */
pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = OL_TXRX_IPA_UC_WLAN_RX_HDR_LEN;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0;
pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1;
pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD;
pipe_in.sys.desc_fifo_sz = ipa_desc_size + sizeof(struct sps_iovec);
pipe_in.sys.notify = ipa_w2i_cb;
if (!is_rm_enabled) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: IPA RM DISABLED, IPA AWAKE", __func__);
pipe_in.sys.keep_ipa_awake = true;
}
pipe_in.u.ul.rdy_ring_base_pa = ipa_res->rx_rdy_ring_base_paddr;
pipe_in.u.ul.rdy_ring_size = ipa_res->rx_rdy_ring_size;
pipe_in.u.ul.rdy_ring_rp_pa = ipa_res->rx_proc_done_idx_paddr;
OL_TXRX_IPA_WDI2_SET(pipe_in, ipa_res);
#ifdef FEATURE_METERING
pipe_in.wdi_notify = ipa_wdi_meter_notifier_cb;
#endif
ret = ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"ipa_connect_wdi_pipe: Rx pipe setup failed: ret=%d", ret);
return QDF_STATUS_E_FAILURE;
}
ipa_res->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa;
ipa_res->rx_pipe_handle = pipe_out.clnt_hdl;
*p_rx_pipe_handle = pipe_out.clnt_hdl;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"RX: %s 0x%x, %s %d, %s 0x%x, %s 0x%x",
"rdy_ring_base_pa",
(unsigned int)pipe_in.u.ul.rdy_ring_base_pa,
"rdy_ring_size",
pipe_in.u.ul.rdy_ring_size,
"rdy_ring_rp_pa",
(unsigned int)pipe_in.u.ul.rdy_ring_rp_pa,
"rx_ready_doorbell_paddr",
(unsigned int)ipa_res->rx_ready_doorbell_paddr);
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_cleanup() - Disconnect IPA pipes
* @tx_pipe_handle: Tx pipe handle
* @rx_pipe_handle: Rx pipe handle
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_cleanup(uint32_t tx_pipe_handle, uint32_t rx_pipe_handle)
{
int ret;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Disconnect TX PIPE tx_pipe_handle=0x%x",
__func__, tx_pipe_handle);
ret = ipa_disconnect_wdi_pipe(tx_pipe_handle);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"ipa_disconnect_wdi_pipe: Tx pipe cleanup failed: ret=%d",
ret);
return QDF_STATUS_E_FAILURE;
}
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Disconnect RX PIPE rx_pipe_handle=0x%x",
__func__, rx_pipe_handle);
ret = ipa_disconnect_wdi_pipe(rx_pipe_handle);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"ipa_disconnect_wdi_pipe: Rx pipe cleanup failed: ret=%d",
ret);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_remove_ipa_header() - Remove a specific header from IPA
* @name: Name of the header to be removed
*
* Return: QDF_STATUS
*/
static QDF_STATUS ol_txrx_ipa_remove_header(char *name)
{
struct ipa_ioc_get_hdr hdrlookup;
int ret = 0, len;
struct ipa_ioc_del_hdr *ipa_hdr;
qdf_mem_zero(&hdrlookup, sizeof(hdrlookup));
strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name));
ret = ipa_get_hdr(&hdrlookup);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"Hdr deleted already %s, %d", name, ret);
return QDF_STATUS_E_FAILURE;
}
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, "hdl: 0x%x",
hdrlookup.hdl);
len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1;
ipa_hdr = (struct ipa_ioc_del_hdr *)qdf_mem_malloc(len);
if (ipa_hdr == NULL) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"ipa_hdr allocation failed");
return QDF_STATUS_E_FAILURE;
}
ipa_hdr->num_hdls = 1;
ipa_hdr->commit = 0;
ipa_hdr->hdl[0].hdl = hdrlookup.hdl;
ipa_hdr->hdl[0].status = -1;
ret = ipa_del_hdr(ipa_hdr);
if (ret != 0) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"Delete header failed: %d", ret);
qdf_mem_free(ipa_hdr);
return QDF_STATUS_E_FAILURE;
}
qdf_mem_free(ipa_hdr);
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_add_header_info() - Add IPA header for a given interface
* @ifname: Interface name
* @mac_addr: Interface MAC address
* @is_ipv6_enabled: Is IPV6 enabled or not
*
* Return: 0 on success, negativer errno value on error
*/
static int ol_txrx_ipa_add_header_info(char *ifname, uint8_t *mac_addr,
uint8_t session_id, bool is_ipv6_enabled)
{
struct ipa_ioc_add_hdr *ipa_hdr = NULL;
int ret = -EINVAL;
struct ol_txrx_ipa_uc_tx_hdr *uc_tx_hdr = NULL;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"Add Partial hdr: %s, %pM", ifname, mac_addr);
/* dynamically allocate the memory to add the hdrs */
ipa_hdr = qdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr)
+ sizeof(struct ipa_hdr_add));
if (!ipa_hdr) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: ipa_hdr allocation failed", ifname);
ret = -ENOMEM;
goto end;
}
ipa_hdr->commit = 0;
ipa_hdr->num_hdrs = 1;
uc_tx_hdr = (struct ol_txrx_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN);
memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
uc_tx_hdr->ipa_hd.vdev_id = session_id;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"ifname=%s, vdev_id=%d",
ifname, uc_tx_hdr->ipa_hd.vdev_id);
snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV4_NAME_EXT);
ipa_hdr->hdr[0].hdr_len = OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN;
ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II;
ipa_hdr->hdr[0].is_partial = 1;
ipa_hdr->hdr[0].hdr_hdl = 0;
ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
ipa_hdr->hdr[0].eth2_ofst = OL_TXRX_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
ret = ipa_add_hdr(ipa_hdr);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s IPv4 add hdr failed: %d", ifname, ret);
goto end;
}
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: IPv4 hdr_hdl: 0x%x",
ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
if (is_ipv6_enabled) {
snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV6_NAME_EXT);
uc_tx_hdr = (struct ol_txrx_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6);
ret = ipa_add_hdr(ipa_hdr);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: IPv6 add hdr failed: %d", ifname, ret);
goto clean_ipv4_hdr;
}
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: IPv6 hdr_hdl: 0x%x",
ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
}
qdf_mem_free(ipa_hdr);
return ret;
clean_ipv4_hdr:
snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV4_NAME_EXT);
ol_txrx_ipa_remove_header(ipa_hdr->hdr[0].name);
end:
if (ipa_hdr)
qdf_mem_free(ipa_hdr);
return ret;
}
/**
* ol_txrx_ipa_register_interface() - register IPA interface
* @ifname: Interface name
* @prod_client: IPA prod client type
* @cons_client: IPA cons client type
* @session_id: Session ID
* @is_ipv6_enabled: Is IPV6 enabled or not
*
* Return: 0 on success, negative errno on error
*/
static int ol_txrx_ipa_register_interface(char *ifname,
enum ipa_client_type prod_client,
enum ipa_client_type cons_client,
uint8_t session_id,
bool is_ipv6_enabled)
{
struct ipa_tx_intf tx_intf;
struct ipa_rx_intf rx_intf;
struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX];
char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX];
int num_prop = 1;
int ret = 0;
if (is_ipv6_enabled)
num_prop++;
/* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */
tx_prop =
qdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop);
if (!tx_prop) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"tx_prop allocation failed");
goto register_interface_fail;
}
/* Allocate RX properties, 1 each for IPv4 & IPv6 */
rx_prop =
qdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop);
if (!rx_prop) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"rx_prop allocation failed");
goto register_interface_fail;
}
qdf_mem_zero(&tx_intf, sizeof(tx_intf));
qdf_mem_zero(&rx_intf, sizeof(rx_intf));
snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV4_NAME_EXT);
snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV6_NAME_EXT);
rx_prop[IPA_IP_v4].ip = IPA_IP_v4;
rx_prop[IPA_IP_v4].src_pipe = prod_client;
rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
/*
* Interface ID is 3rd byte in the CLD header. Add the meta data and
* mask to identify the interface in IPA hardware
*/
rx_prop[IPA_IP_v4].attrib.meta_data =
htonl(session_id << 16);
rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
rx_intf.num_props++;
if (is_ipv6_enabled) {
rx_prop[IPA_IP_v6].ip = IPA_IP_v6;
rx_prop[IPA_IP_v6].src_pipe = prod_client;
rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
rx_prop[IPA_IP_v6].attrib.attrib_mask = IPA_FLT_META_DATA;
rx_prop[IPA_IP_v6].attrib.meta_data =
htonl(session_id << 16);
rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
rx_intf.num_props++;
}
tx_prop[IPA_IP_v4].ip = IPA_IP_v4;
tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS;
tx_prop[IPA_IP_v4].alt_dst_pipe = cons_client;
strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name,
IPA_RESOURCE_NAME_MAX);
tx_intf.num_props++;
if (is_ipv6_enabled) {
tx_prop[IPA_IP_v6].ip = IPA_IP_v6;
tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS;
tx_prop[IPA_IP_v6].alt_dst_pipe = cons_client;
strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name,
IPA_RESOURCE_NAME_MAX);
tx_intf.num_props++;
}
tx_intf.prop = tx_prop;
rx_intf.prop = rx_prop;
/* Call the ipa api to register interface */
ret = ipa_register_intf(ifname, &tx_intf, &rx_intf);
register_interface_fail:
qdf_mem_free(tx_prop);
qdf_mem_free(rx_prop);
return ret;
}
/**
* ol_txrx_ipa_setup_iface() - Setup IPA header and register interface
* @ifname: Interface name
* @mac_addr: Interface MAC address
* @prod_client: IPA prod client type
* @cons_client: IPA cons client type
* @session_id: Session ID
* @is_ipv6_enabled: Is IPV6 enabled or not
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_setup_iface(char *ifname, uint8_t *mac_addr,
enum ipa_client_type prod_client,
enum ipa_client_type cons_client,
uint8_t session_id, bool is_ipv6_enabled)
{
int ret;
ret = ol_txrx_ipa_add_header_info(ifname, mac_addr, session_id,
is_ipv6_enabled);
if (ret)
return QDF_STATUS_E_FAILURE;
/* Configure the TX and RX pipes filter rules */
ret = ol_txrx_ipa_register_interface(ifname, prod_client, cons_client,
session_id, is_ipv6_enabled);
if (ret)
return QDF_STATUS_E_FAILURE;
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_cleanup_iface() - Cleanup IPA header and deregister interface
* @ifname: Interface name
* @is_ipv6_enabled: Is IPV6 enabled or not
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_cleanup_iface(char *ifname, bool is_ipv6_enabled)
{
char name_ipa[IPA_RESOURCE_NAME_MAX];
int ret;
/* Remove the headers */
snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV4_NAME_EXT);
ol_txrx_ipa_remove_header(name_ipa);
if (is_ipv6_enabled) {
snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
ifname, OL_TXRX_IPA_IPV6_NAME_EXT);
ol_txrx_ipa_remove_header(name_ipa);
}
/* unregister the interface with IPA */
ret = ipa_deregister_intf(ifname);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: ipa_deregister_intf fail: %d",
ifname, ret);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_enable_pipes() - Enable and resume traffic on Tx/Rx pipes
* @pdev: handle to the device instance
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_enable_pipes(struct cdp_pdev *ppdev)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
int result;
/* ACTIVATE TX PIPE */
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Enable TX PIPE(tx_pipe_handle=%d)",
__func__, ipa_res->tx_pipe_handle);
result = ipa_enable_wdi_pipe(ipa_res->tx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Enable TX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
result = ipa_resume_wdi_pipe(ipa_res->tx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Resume TX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
ol_txrx_ipa_uc_set_active((struct cdp_pdev *)pdev, true, true);
/* ACTIVATE RX PIPE */
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Enable RX PIPE(rx_pipe_handle=%d)",
__func__, ipa_res->rx_pipe_handle);
result = ipa_enable_wdi_pipe(ipa_res->rx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Enable RX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
result = ipa_resume_wdi_pipe(ipa_res->rx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Resume RX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
ol_txrx_ipa_uc_set_active((struct cdp_pdev *)pdev, true, false);
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_uc_disable_pipes() – Suspend traffic and disable Tx/Rx pipes
* @pdev: handle to the device instance
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_disable_pipes(struct cdp_pdev *ppdev)
{
ol_txrx_pdev_handle pdev = (ol_txrx_pdev_handle)ppdev;
struct ol_txrx_ipa_resources *ipa_res = &pdev->ipa_resource;
int result;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Disable RX PIPE", __func__);
result = ipa_suspend_wdi_pipe(ipa_res->rx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Suspend RX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
result = ipa_disable_wdi_pipe(ipa_res->rx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Disable RX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Disable TX PIPE", __func__);
result = ipa_suspend_wdi_pipe(ipa_res->tx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Suspend TX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
result = ipa_disable_wdi_pipe(ipa_res->tx_pipe_handle);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Disable TX PIPE fail, code %d",
__func__, result);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* ol_txrx_ipa_set_perf_level() - Set IPA clock bandwidth based on data rates
* @client: Client type
* @max_supported_bw_mbps: Maximum bandwidth needed (in Mbps)
*
* Return: QDF_STATUS
*/
QDF_STATUS ol_txrx_ipa_set_perf_level(int client,
uint32_t max_supported_bw_mbps)
{
struct ipa_rm_perf_profile profile;
int result;
profile.max_supported_bandwidth_mbps = max_supported_bw_mbps;
result = ipa_rm_set_perf_profile(client, &profile);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"Set perf profile failed, code %d", result);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
#ifdef FEATURE_METERING
QDF_STATUS ol_txrx_ipa_uc_get_share_stats(struct cdp_pdev *ppdev,
uint8_t reset_stats)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
int result;
result = htt_h2t_ipa_uc_get_share_stats(pdev->htt_pdev, reset_stats);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"Get IPA sharing stats failed, code %d", result);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS ol_txrx_ipa_uc_set_quota(struct cdp_pdev *ppdev,
uint64_t quota_bytes)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
int result;
result = htt_h2t_ipa_uc_set_quota(pdev->htt_pdev, quota_bytes);
if (result) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"Set IPA quota failed, code %d", result);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
#endif
#endif /* IPA_UC_OFFLOAD */