linux/arch/ia64/lib/csum_partial_copy.c
<<
>>
Prefs
   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
  16/*
  17 * XXX Fixme: those 2 inlines are meant for debugging and will go away
  18 */
  19static inline unsigned
  20short from64to16(unsigned long x)
  21{
  22        /* add up 32-bit words for 33 bits */
  23        x = (x & 0xffffffff) + (x >> 32);
  24        /* add up 16-bit and 17-bit words for 17+c bits */
  25        x = (x & 0xffff) + (x >> 16);
  26        /* add up 16-bit and 2-bit for 16+c bit */
  27        x = (x & 0xffff) + (x >> 16);
  28        /* add up carry.. */
  29        x = (x & 0xffff) + (x >> 16);
  30        return x;
  31}
  32
  33static inline
  34unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
  35{
  36        int odd, count;
  37        unsigned long result = (unsigned long)psum;
  38
  39        if (len <= 0)
  40                goto out;
  41        odd = 1 & (unsigned long) buff;
  42        if (odd) {
  43                result = *buff << 8;
  44                len--;
  45                buff++;
  46        }
  47        count = len >> 1;               /* nr of 16-bit words.. */
  48        if (count) {
  49                if (2 & (unsigned long) buff) {
  50                        result += *(unsigned short *) buff;
  51                        count--;
  52                        len -= 2;
  53                        buff += 2;
  54                }
  55                count >>= 1;            /* nr of 32-bit words.. */
  56                if (count) {
  57                        if (4 & (unsigned long) buff) {
  58                                result += *(unsigned int *) buff;
  59                                count--;
  60                                len -= 4;
  61                                buff += 4;
  62                        }
  63                        count >>= 1;    /* nr of 64-bit words.. */
  64                        if (count) {
  65                                unsigned long carry = 0;
  66                                do {
  67                                        unsigned long w = *(unsigned long *) buff;
  68                                        count--;
  69                                        buff += 8;
  70                                        result += carry;
  71                                        result += w;
  72                                        carry = (w > result);
  73                                } while (count);
  74                                result += carry;
  75                                result = (result & 0xffffffff) + (result >> 32);
  76                        }
  77                        if (len & 4) {
  78                                result += *(unsigned int *) buff;
  79                                buff += 4;
  80                        }
  81                }
  82                if (len & 2) {
  83                        result += *(unsigned short *) buff;
  84                        buff += 2;
  85                }
  86        }
  87        if (len & 1)
  88                result += *buff;
  89
  90        result = from64to16(result);
  91
  92        if (odd)
  93                result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
  94
  95out:
  96        return result;
  97}
  98
  99/*
 100 * XXX Fixme
 101 *
 102 * This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS.
 103 * But it's very tricky to get right even in C.
 104 */
 105extern unsigned long do_csum(const unsigned char *, long);
 106
 107__wsum
 108csum_partial_copy_from_user(const void __user *src, void *dst,
 109                                int len, __wsum psum, int *errp)
 110{
 111        unsigned long result;
 112
 113        /* XXX Fixme
 114         * for now we separate the copy from checksum for obvious
 115         * alignment difficulties. Look at the Alpha code and you'll be
 116         * scared.
 117         */
 118
 119        if (__copy_from_user(dst, src, len) != 0 && errp)
 120                *errp = -EFAULT;
 121
 122        result = do_csum(dst, len);
 123
 124        /* add in old sum, and carry.. */
 125        result += (__force u32)psum;
 126        /* 32+c bits -> 32 bits */
 127        result = (result & 0xffffffff) + (result >> 32);
 128        return (__force __wsum)result;
 129}
 130
 131EXPORT_SYMBOL(csum_partial_copy_from_user);
 132
 133__wsum
 134csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
 135{
 136        return csum_partial_copy_from_user((__force const void __user *)src,
 137                                           dst, len, sum, NULL);
 138}
 139
 140EXPORT_SYMBOL(csum_partial_copy_nocheck);
 141