/*
 *
 * If you received this File from Marvell, you may opt to use, redistribute and/or
 * modify this File in accordance with the terms and conditions of the General
 * Public License Version 2, June 1991 (the "GPL License"), a copy of which is
 * available along with the File in the license.txt file or by writing to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
 * on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
 * DISCLAIMED. The GPL License provides additional details about this warranty
 * disclaimer.
 *
 */
#include "oak_net.h"
#include "oak_debug.h"
#include "oak_chksum.h"

/* Name        : oak_chksum_get_config
 * Returns     : netdev_features_t
 * Parameters  : void
 * Description : This function provides Oak Hardware's Checksum Offload
 *               capabilities
 */
netdev_features_t oak_chksum_get_config(void)
{
	netdev_features_t features = OAK_CHKSUM_TYPE;

	/* Oak HW Supports L3 & L3 Checksum Offload and Freagmented frame,
	 * so scatter gather need to be enabled.
	 */
	features |= NETIF_F_SG;

	return features;
}

/* Name        : oak_chksum_get_tx_config
 * Returns     : u32
 * Parameters  : struct sk_buff *skb, u32 *cs_l3, u32 *cs_l4
 * Description : This function returns the Checksum Offload configuration
 *               for the transmit frame.
 */
u32 oak_chksum_get_tx_config(struct sk_buff *skb, u32 *cs_l3,
			     u32 *cs_l4)
{
	u32 retval = 0;
	int prot;

	*cs_l3 = 0;
	*cs_l4 = 0;

	if (skb->ip_summed == CHECKSUM_PARTIAL) {
		prot = oak_net_skb_tx_protocol_type(skb);
		if (prot == L3_L4_CHKSUM) {
			*cs_l3 = 1;
			*cs_l4 = 1;
		} else if (prot == L3_CHKSUM) {
			*cs_l3 = 1;
			*cs_l4 = 0;
		} else {
		/* Setting cs_l3 & cs_l4 to zero is done in error case after
		 * this function returns.
		 */
			retval = 1;
		}
	} else {
		retval = 1;
	}

	return retval;
}

/* Name        : oak_chksum_get_rx_config
 * Returns     : u32
 * Parameters  : struct oak_rx_chan_t *rxc, struct oak_rxs_t *rsr
 * Description : This function returns the current receive frames
 *               checksum state.
 */
int oak_chksum_get_rx_config(oak_rx_chan_t *rxc, oak_rxs_t *rsr)
{
	int retval = CHECKSUM_NONE;

	if (rsr->vlan == 1)
		++rxc->stat.rx_vlan;

	if (rsr->l3_ipv4 == 1 || rsr->l3_ipv6 == 1) {
		if (rsr->l4_prot == OAK_TCP_IP_FRAME ||
		    rsr->l4_prot == OAK_TCP_UDP_FRAME) {
			if (rsr->l4_chk_ok == 1) {
				rxc->stat.rx_l4p_ok++;
				retval = CHECKSUM_UNNECESSARY;
			}
		} else if (rsr->l3_ipv4 == 1 && rsr->ipv4_hdr_ok == 1) {
		/*
		 * Linux documentation for CHECKSUM_PARTIAL
		 * in include/linux/skbuff.h this state may occur on a packet
		 * received directly from another Linux OS,
		 * e.g., a virtualized Linux kernel on the same host,
		 * or it may be set in the input path in GRO or remote checksum
		 * offload.
		 * As per the discussion with the Linux kernel netdev forum
		 * setting ip_summed to PARTIAL on receive is only valid
		 * for software/virtual devices, never real HW.
		 * For a frame where the checksum is not verified by the HW,
		 * the flag will be set as CHECKSUM_NONE such that
		 * linux netdev layer verifies the same.
		 */
			rxc->stat.rx_ip4_ok++;
		}
	}

	return retval;
}
