162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Network Checksum & Copy routine
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1999, 2003-2004 Hewlett-Packard Co
662306a36Sopenharmony_ci *	Stephane Eranian <eranian@hpl.hp.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Most of the code has been imported from Linux/Alpha
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/string.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <net/checksum.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * XXX Fixme: those 2 inlines are meant for debugging and will go away
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_cistatic inline unsigned
2162306a36Sopenharmony_cishort from64to16(unsigned long x)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	/* add up 32-bit words for 33 bits */
2462306a36Sopenharmony_ci	x = (x & 0xffffffff) + (x >> 32);
2562306a36Sopenharmony_ci	/* add up 16-bit and 17-bit words for 17+c bits */
2662306a36Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
2762306a36Sopenharmony_ci	/* add up 16-bit and 2-bit for 16+c bit */
2862306a36Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
2962306a36Sopenharmony_ci	/* add up carry.. */
3062306a36Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
3162306a36Sopenharmony_ci	return x;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline
3562306a36Sopenharmony_ciunsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	int odd, count;
3862306a36Sopenharmony_ci	unsigned long result = (unsigned long)psum;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (len <= 0)
4162306a36Sopenharmony_ci		goto out;
4262306a36Sopenharmony_ci	odd = 1 & (unsigned long) buff;
4362306a36Sopenharmony_ci	if (odd) {
4462306a36Sopenharmony_ci		result = *buff << 8;
4562306a36Sopenharmony_ci		len--;
4662306a36Sopenharmony_ci		buff++;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci	count = len >> 1;		/* nr of 16-bit words.. */
4962306a36Sopenharmony_ci	if (count) {
5062306a36Sopenharmony_ci		if (2 & (unsigned long) buff) {
5162306a36Sopenharmony_ci			result += *(unsigned short *) buff;
5262306a36Sopenharmony_ci			count--;
5362306a36Sopenharmony_ci			len -= 2;
5462306a36Sopenharmony_ci			buff += 2;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci		count >>= 1;		/* nr of 32-bit words.. */
5762306a36Sopenharmony_ci		if (count) {
5862306a36Sopenharmony_ci			if (4 & (unsigned long) buff) {
5962306a36Sopenharmony_ci				result += *(unsigned int *) buff;
6062306a36Sopenharmony_ci				count--;
6162306a36Sopenharmony_ci				len -= 4;
6262306a36Sopenharmony_ci				buff += 4;
6362306a36Sopenharmony_ci			}
6462306a36Sopenharmony_ci			count >>= 1;	/* nr of 64-bit words.. */
6562306a36Sopenharmony_ci			if (count) {
6662306a36Sopenharmony_ci				unsigned long carry = 0;
6762306a36Sopenharmony_ci				do {
6862306a36Sopenharmony_ci					unsigned long w = *(unsigned long *) buff;
6962306a36Sopenharmony_ci					count--;
7062306a36Sopenharmony_ci					buff += 8;
7162306a36Sopenharmony_ci					result += carry;
7262306a36Sopenharmony_ci					result += w;
7362306a36Sopenharmony_ci					carry = (w > result);
7462306a36Sopenharmony_ci				} while (count);
7562306a36Sopenharmony_ci				result += carry;
7662306a36Sopenharmony_ci				result = (result & 0xffffffff) + (result >> 32);
7762306a36Sopenharmony_ci			}
7862306a36Sopenharmony_ci			if (len & 4) {
7962306a36Sopenharmony_ci				result += *(unsigned int *) buff;
8062306a36Sopenharmony_ci				buff += 4;
8162306a36Sopenharmony_ci			}
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci		if (len & 2) {
8462306a36Sopenharmony_ci			result += *(unsigned short *) buff;
8562306a36Sopenharmony_ci			buff += 2;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci	if (len & 1)
8962306a36Sopenharmony_ci		result += *buff;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	result = from64to16(result);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (odd)
9462306a36Sopenharmony_ci		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciout:
9762306a36Sopenharmony_ci	return result;
9862306a36Sopenharmony_ci}
99