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