xref: /OK3568_Linux_fs/kernel/arch/parisc/lib/checksum.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * INET		An implementation of the TCP/IP protocol suite for the LINUX
4*4882a593Smuzhiyun  *		operating system.  INET is implemented using the  BSD Socket
5*4882a593Smuzhiyun  *		interface as the means of communication with the user level.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *		MIPS specific IP/TCP/UDP checksumming routines
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Authors:	Ralf Baechle, <ralf@waldorf-gmbh.de>
10*4882a593Smuzhiyun  *		Lots of code moved from tcp.c and ip.c; see those files
11*4882a593Smuzhiyun  *		for more names.
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/types.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <net/checksum.h>
17*4882a593Smuzhiyun #include <asm/byteorder.h>
18*4882a593Smuzhiyun #include <asm/string.h>
19*4882a593Smuzhiyun #include <linux/uaccess.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define addc(_t,_r)                     \
22*4882a593Smuzhiyun 	__asm__ __volatile__ (          \
23*4882a593Smuzhiyun "       add             %0, %1, %0\n"   \
24*4882a593Smuzhiyun "       addc            %0, %%r0, %0\n" \
25*4882a593Smuzhiyun 	: "=r"(_t)                      \
26*4882a593Smuzhiyun 	: "r"(_r), "0"(_t));
27*4882a593Smuzhiyun 
from32to16(unsigned int x)28*4882a593Smuzhiyun static inline unsigned short from32to16(unsigned int x)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun 	/* 32 bits --> 16 bits + carry */
31*4882a593Smuzhiyun 	x = (x & 0xffff) + (x >> 16);
32*4882a593Smuzhiyun 	/* 16 bits + carry --> 16 bits including carry */
33*4882a593Smuzhiyun 	x = (x & 0xffff) + (x >> 16);
34*4882a593Smuzhiyun 	return (unsigned short)x;
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun 
do_csum(const unsigned char * buff,int len)37*4882a593Smuzhiyun static inline unsigned int do_csum(const unsigned char * buff, int len)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	int odd, count;
40*4882a593Smuzhiyun 	unsigned int result = 0;
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	if (len <= 0)
43*4882a593Smuzhiyun 		goto out;
44*4882a593Smuzhiyun 	odd = 1 & (unsigned long) buff;
45*4882a593Smuzhiyun 	if (odd) {
46*4882a593Smuzhiyun 		result = be16_to_cpu(*buff);
47*4882a593Smuzhiyun 		len--;
48*4882a593Smuzhiyun 		buff++;
49*4882a593Smuzhiyun 	}
50*4882a593Smuzhiyun 	count = len >> 1;		/* nr of 16-bit words.. */
51*4882a593Smuzhiyun 	if (count) {
52*4882a593Smuzhiyun 		if (2 & (unsigned long) buff) {
53*4882a593Smuzhiyun 			result += *(unsigned short *) buff;
54*4882a593Smuzhiyun 			count--;
55*4882a593Smuzhiyun 			len -= 2;
56*4882a593Smuzhiyun 			buff += 2;
57*4882a593Smuzhiyun 		}
58*4882a593Smuzhiyun 		count >>= 1;		/* nr of 32-bit words.. */
59*4882a593Smuzhiyun 		if (count) {
60*4882a593Smuzhiyun 			while (count >= 4) {
61*4882a593Smuzhiyun 				unsigned int r1, r2, r3, r4;
62*4882a593Smuzhiyun 				r1 = *(unsigned int *)(buff + 0);
63*4882a593Smuzhiyun 				r2 = *(unsigned int *)(buff + 4);
64*4882a593Smuzhiyun 				r3 = *(unsigned int *)(buff + 8);
65*4882a593Smuzhiyun 				r4 = *(unsigned int *)(buff + 12);
66*4882a593Smuzhiyun 				addc(result, r1);
67*4882a593Smuzhiyun 				addc(result, r2);
68*4882a593Smuzhiyun 				addc(result, r3);
69*4882a593Smuzhiyun 				addc(result, r4);
70*4882a593Smuzhiyun 				count -= 4;
71*4882a593Smuzhiyun 				buff += 16;
72*4882a593Smuzhiyun 			}
73*4882a593Smuzhiyun 			while (count) {
74*4882a593Smuzhiyun 				unsigned int w = *(unsigned int *) buff;
75*4882a593Smuzhiyun 				count--;
76*4882a593Smuzhiyun 				buff += 4;
77*4882a593Smuzhiyun 				addc(result, w);
78*4882a593Smuzhiyun 			}
79*4882a593Smuzhiyun 			result = (result & 0xffff) + (result >> 16);
80*4882a593Smuzhiyun 		}
81*4882a593Smuzhiyun 		if (len & 2) {
82*4882a593Smuzhiyun 			result += *(unsigned short *) buff;
83*4882a593Smuzhiyun 			buff += 2;
84*4882a593Smuzhiyun 		}
85*4882a593Smuzhiyun 	}
86*4882a593Smuzhiyun 	if (len & 1)
87*4882a593Smuzhiyun 		result += le16_to_cpu(*buff);
88*4882a593Smuzhiyun 	result = from32to16(result);
89*4882a593Smuzhiyun 	if (odd)
90*4882a593Smuzhiyun 		result = swab16(result);
91*4882a593Smuzhiyun out:
92*4882a593Smuzhiyun 	return result;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun /*
96*4882a593Smuzhiyun  * computes a partial checksum, e.g. for TCP/UDP fragments
97*4882a593Smuzhiyun  */
98*4882a593Smuzhiyun /*
99*4882a593Smuzhiyun  * why bother folding?
100*4882a593Smuzhiyun  */
csum_partial(const void * buff,int len,__wsum sum)101*4882a593Smuzhiyun __wsum csum_partial(const void *buff, int len, __wsum sum)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	unsigned int result = do_csum(buff, len);
104*4882a593Smuzhiyun 	addc(result, sum);
105*4882a593Smuzhiyun 	return (__force __wsum)from32to16(result);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun EXPORT_SYMBOL(csum_partial);
109