1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Network Checksum & Copy routine
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1999, 2003-2004 Hewlett-Packard Co
61da177e4SLinus Torvalds  *	Stephane Eranian <eranian@hpl.hp.com>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Most of the code has been imported from Linux/Alpha
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/types.h>
131da177e4SLinus Torvalds #include <linux/string.h>
141da177e4SLinus Torvalds 
15174e1ea8SAl Viro #include <net/checksum.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /*
181da177e4SLinus Torvalds  * XXX Fixme: those 2 inlines are meant for debugging and will go away
191da177e4SLinus Torvalds  */
201da177e4SLinus Torvalds static inline unsigned
from64to16(unsigned long x)211da177e4SLinus Torvalds short from64to16(unsigned long x)
221da177e4SLinus Torvalds {
231da177e4SLinus Torvalds 	/* add up 32-bit words for 33 bits */
241da177e4SLinus Torvalds 	x = (x & 0xffffffff) + (x >> 32);
251da177e4SLinus Torvalds 	/* add up 16-bit and 17-bit words for 17+c bits */
261da177e4SLinus Torvalds 	x = (x & 0xffff) + (x >> 16);
271da177e4SLinus Torvalds 	/* add up 16-bit and 2-bit for 16+c bit */
281da177e4SLinus Torvalds 	x = (x & 0xffff) + (x >> 16);
291da177e4SLinus Torvalds 	/* add up carry.. */
301da177e4SLinus Torvalds 	x = (x & 0xffff) + (x >> 16);
311da177e4SLinus Torvalds 	return x;
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static inline
do_csum_c(const unsigned char * buff,int len,unsigned int psum)351da177e4SLinus Torvalds unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds 	int odd, count;
381da177e4SLinus Torvalds 	unsigned long result = (unsigned long)psum;
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds 	if (len <= 0)
411da177e4SLinus Torvalds 		goto out;
421da177e4SLinus Torvalds 	odd = 1 & (unsigned long) buff;
431da177e4SLinus Torvalds 	if (odd) {
441da177e4SLinus Torvalds 		result = *buff << 8;
451da177e4SLinus Torvalds 		len--;
461da177e4SLinus Torvalds 		buff++;
471da177e4SLinus Torvalds 	}
481da177e4SLinus Torvalds 	count = len >> 1;		/* nr of 16-bit words.. */
491da177e4SLinus Torvalds 	if (count) {
501da177e4SLinus Torvalds 		if (2 & (unsigned long) buff) {
511da177e4SLinus Torvalds 			result += *(unsigned short *) buff;
521da177e4SLinus Torvalds 			count--;
531da177e4SLinus Torvalds 			len -= 2;
541da177e4SLinus Torvalds 			buff += 2;
551da177e4SLinus Torvalds 		}
561da177e4SLinus Torvalds 		count >>= 1;		/* nr of 32-bit words.. */
571da177e4SLinus Torvalds 		if (count) {
581da177e4SLinus Torvalds 			if (4 & (unsigned long) buff) {
591da177e4SLinus Torvalds 				result += *(unsigned int *) buff;
601da177e4SLinus Torvalds 				count--;
611da177e4SLinus Torvalds 				len -= 4;
621da177e4SLinus Torvalds 				buff += 4;
631da177e4SLinus Torvalds 			}
641da177e4SLinus Torvalds 			count >>= 1;	/* nr of 64-bit words.. */
651da177e4SLinus Torvalds 			if (count) {
661da177e4SLinus Torvalds 				unsigned long carry = 0;
671da177e4SLinus Torvalds 				do {
681da177e4SLinus Torvalds 					unsigned long w = *(unsigned long *) buff;
691da177e4SLinus Torvalds 					count--;
701da177e4SLinus Torvalds 					buff += 8;
711da177e4SLinus Torvalds 					result += carry;
721da177e4SLinus Torvalds 					result += w;
731da177e4SLinus Torvalds 					carry = (w > result);
741da177e4SLinus Torvalds 				} while (count);
751da177e4SLinus Torvalds 				result += carry;
761da177e4SLinus Torvalds 				result = (result & 0xffffffff) + (result >> 32);
771da177e4SLinus Torvalds 			}
781da177e4SLinus Torvalds 			if (len & 4) {
791da177e4SLinus Torvalds 				result += *(unsigned int *) buff;
801da177e4SLinus Torvalds 				buff += 4;
811da177e4SLinus Torvalds 			}
821da177e4SLinus Torvalds 		}
831da177e4SLinus Torvalds 		if (len & 2) {
841da177e4SLinus Torvalds 			result += *(unsigned short *) buff;
851da177e4SLinus Torvalds 			buff += 2;
861da177e4SLinus Torvalds 		}
871da177e4SLinus Torvalds 	}
881da177e4SLinus Torvalds 	if (len & 1)
891da177e4SLinus Torvalds 		result += *buff;
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	result = from64to16(result);
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	if (odd)
941da177e4SLinus Torvalds 		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds out:
971da177e4SLinus Torvalds 	return result;
981da177e4SLinus Torvalds }
99