1/*
2 * Network Checksum & Copy routine
3 *
4 * Copyright (C) 1999, 2003-2004 Hewlett-Packard Co
5 *	Stephane Eranian <eranian@hpl.hp.com>
6 *
7 * Most of the code has been imported from Linux/Alpha
8 */
9
10#include <linux/module.h>
11#include <linux/types.h>
12#include <linux/string.h>
13
14#include <asm/uaccess.h>
15
16static inline unsigned
17short from64to16(unsigned long x)
18{
19	/* add up 32-bit words for 33 bits */
20	x = (x & 0xffffffff) + (x >> 32);
21	/* add up 16-bit and 17-bit words for 17+c bits */
22	x = (x & 0xffff) + (x >> 16);
23	/* add up 16-bit and 2-bit for 16+c bit */
24	x = (x & 0xffff) + (x >> 16);
25	/* add up carry.. */
26	x = (x & 0xffff) + (x >> 16);
27	return x;
28}
29
30static inline
31unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
32{
33	int odd, count;
34	unsigned long result = (unsigned long)psum;
35
36	if (len <= 0)
37		goto out;
38	odd = 1 & (unsigned long) buff;
39	if (odd) {
40		result = *buff << 8;
41		len--;
42		buff++;
43	}
44	count = len >> 1;		/* nr of 16-bit words.. */
45	if (count) {
46		if (2 & (unsigned long) buff) {
47			result += *(unsigned short *) buff;
48			count--;
49			len -= 2;
50			buff += 2;
51		}
52		count >>= 1;		/* nr of 32-bit words.. */
53		if (count) {
54			if (4 & (unsigned long) buff) {
55				result += *(unsigned int *) buff;
56				count--;
57				len -= 4;
58				buff += 4;
59			}
60			count >>= 1;	/* nr of 64-bit words.. */
61			if (count) {
62				unsigned long carry = 0;
63				do {
64					unsigned long w = *(unsigned long *) buff;
65					count--;
66					buff += 8;
67					result += carry;
68					result += w;
69					carry = (w > result);
70				} while (count);
71				result += carry;
72				result = (result & 0xffffffff) + (result >> 32);
73			}
74			if (len & 4) {
75				result += *(unsigned int *) buff;
76				buff += 4;
77			}
78		}
79		if (len & 2) {
80			result += *(unsigned short *) buff;
81			buff += 2;
82		}
83	}
84	if (len & 1)
85		result += *buff;
86
87	result = from64to16(result);
88
89	if (odd)
90		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
91
92out:
93	return result;
94}
95
96extern unsigned long do_csum(const unsigned char *, long);
97
98__wsum
99csum_partial_copy_from_user(const void __user *src, void *dst,
100				int len, __wsum psum, int *errp)
101{
102	unsigned long result;
103
104
105	if (__copy_from_user(dst, src, len) != 0 && errp)
106		*errp = -EFAULT;
107
108	result = do_csum(dst, len);
109
110	/* add in old sum, and carry.. */
111	result += (__force u32)psum;
112	/* 32+c bits -> 32 bits */
113	result = (result & 0xffffffff) + (result >> 32);
114	return (__force __wsum)result;
115}
116
117EXPORT_SYMBOL(csum_partial_copy_from_user);
118
119__wsum
120csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
121{
122	return csum_partial_copy_from_user((__force const void __user *)src,
123					   dst, len, sum, NULL);
124}
125
126EXPORT_SYMBOL(csum_partial_copy_nocheck);
127