File: /usr/src/linux/arch/ia64/lib/csum_partial_copy.c

1     /*
2      * Network Checksum & Copy routine
3      *
4      * Copyright (C) 1999 Hewlett-Packard Co
5      * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
6      *
7      * Most of the code has been imported from Linux/Alpha
8      */
9     
10     #include <linux/types.h>
11     #include <linux/string.h>
12     
13     #include <asm/uaccess.h>
14     
15     /*
16      * XXX Fixme: those 2 inlines are meant for debugging and will go away
17      */
18     static inline unsigned
19     short from64to16(unsigned long x)
20     {
21     	/* add up 32-bit words for 33 bits */
22     	x = (x & 0xffffffff) + (x >> 32);
23     	/* add up 16-bit and 17-bit words for 17+c bits */
24     	x = (x & 0xffff) + (x >> 16);
25     	/* add up 16-bit and 2-bit for 16+c bit */
26     	x = (x & 0xffff) + (x >> 16);
27     	/* add up carry.. */
28     	x = (x & 0xffff) + (x >> 16);
29     	return x;
30     }
31     
32     static inline
33     unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
34     {
35     	int odd, count;
36     	unsigned long result = (unsigned long)psum;
37     
38     	if (len <= 0)
39     		goto out;
40     	odd = 1 & (unsigned long) buff;
41     	if (odd) {
42     		result = *buff << 8;
43     		len--;
44     		buff++;
45     	}
46     	count = len >> 1;		/* nr of 16-bit words.. */
47     	if (count) {
48     		if (2 & (unsigned long) buff) {
49     			result += *(unsigned short *) buff;
50     			count--;
51     			len -= 2;
52     			buff += 2;
53     		}
54     		count >>= 1;		/* nr of 32-bit words.. */
55     		if (count) {
56     			if (4 & (unsigned long) buff) {
57     				result += *(unsigned int *) buff;
58     				count--;
59     				len -= 4;
60     				buff += 4;
61     			}
62     			count >>= 1;	/* nr of 64-bit words.. */
63     			if (count) {
64     				unsigned long carry = 0;
65     				do {
66     					unsigned long w = *(unsigned long *) buff;
67     					count--;
68     					buff += 8;
69     					result += carry;
70     					result += w;
71     					carry = (w > result);
72     				} while (count);
73     				result += carry;
74     				result = (result & 0xffffffff) + (result >> 32);
75     			}
76     			if (len & 4) {
77     				result += *(unsigned int *) buff;
78     				buff += 4;
79     			}
80     		}
81     		if (len & 2) {
82     			result += *(unsigned short *) buff;
83     			buff += 2;
84     		}
85     	}
86     	if (len & 1)
87     		result += *buff;
88     
89     	result = from64to16(result);
90     
91     	if (odd)
92     		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
93     
94     out:
95     	return result;
96     }
97     
98     /*
99      * XXX Fixme
100      *
101      * This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS.
102      * But it's very tricky to get right even in C.
103      */
104     extern unsigned long do_csum(const unsigned char *, long);
105     
106     static unsigned int
107     do_csum_partial_copy_from_user (const char *src, char *dst, int len,
108     				unsigned int psum, int *errp)
109     {
110     	unsigned long result;
111     
112     	/* XXX Fixme
113     	 * for now we separate the copy from checksum for obvious
114     	 * alignment difficulties. Look at the Alpha code and you'll be
115     	 * scared.
116     	 */
117     
118     	if (__copy_from_user(dst, src, len) != 0 && errp)
119     		*errp = -EFAULT;
120     
121     	result = do_csum(dst, len);
122     
123     	/* add in old sum, and carry.. */
124     	result += psum;
125     	/* 32+c bits -> 32 bits */
126     	result = (result & 0xffffffff) + (result >> 32);
127     	return result;
128     }
129     
130     unsigned int
131     csum_partial_copy_from_user(const char *src, char *dst, int len,
132     			    unsigned int sum, int *errp)
133     {
134     	if (!access_ok(src, len, VERIFY_READ)) {
135     		*errp = -EFAULT;
136     		memset(dst, 0, len);
137     		return sum;
138     	}
139     
140     	return do_csum_partial_copy_from_user(src, dst, len, sum, errp);
141     }
142     
143     unsigned int
144     csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum)
145     {
146     	return do_csum_partial_copy_from_user(src, dst, len, sum, NULL);
147     }
148     
149     unsigned int
150     csum_partial_copy (const char *src, char *dst, int len, unsigned int sum)
151     {
152     	unsigned int ret;
153     	int error = 0;
154     
155     	ret = do_csum_partial_copy_from_user(src, dst, len, sum, &error);
156     	if (error)
157     		printk("csum_partial_copy_old(): tell mingo to convert me!\n");
158     
159     	return ret;
160     }
161     
162