linux/arch/v850/lib/checksum.c
<<
>>
Prefs
   1/*
   2 * INET         An implementation of the TCP/IP protocol suite for the LINUX
   3 *              operating system.  INET is implemented using the  BSD Socket
   4 *              interface as the means of communication with the user level.
   5 *
   6 *              MIPS specific IP/TCP/UDP checksumming routines
   7 *
   8 * Authors:     Ralf Baechle, <ralf@waldorf-gmbh.de>
   9 *              Lots of code moved from tcp.c and ip.c; see those files
  10 *              for more names.
  11 *
  12 *              This program is free software; you can redistribute it and/or
  13 *              modify it under the terms of the GNU General Public License
  14 *              as published by the Free Software Foundation; either version
  15 *              2 of the License, or (at your option) any later version.
  16 *
  17 * $Id: checksum.c,v 1.1 2002/09/28 14:58:40 gerg Exp $
  18 */
  19#include <net/checksum.h>
  20#include <linux/module.h>
  21#include <linux/types.h>
  22#include <asm/byteorder.h>
  23#include <asm/string.h>
  24#include <asm/uaccess.h>
  25
  26static inline unsigned short from32to16 (unsigned long sum)
  27{
  28        unsigned int result;
  29        /*
  30                                %0              %1
  31              hsw %1, %0        H     L         L     H
  32              add %1, %0        H     L         H+L+C H+L
  33        */
  34        asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum));
  35        return result >> 16;
  36}
  37
  38static inline unsigned int do_csum(const unsigned char * buff, int len)
  39{
  40        int odd, count;
  41        unsigned int result = 0;
  42
  43        if (len <= 0)
  44                goto out;
  45        odd = 1 & (unsigned long) buff;
  46        if (odd) {
  47                result = be16_to_cpu(*buff);
  48                len--;
  49                buff++;
  50        }
  51        count = len >> 1;               /* nr of 16-bit words.. */
  52        if (count) {
  53                if (2 & (unsigned long) buff) {
  54                        result += *(unsigned short *) buff;
  55                        count--;
  56                        len -= 2;
  57                        buff += 2;
  58                }
  59                count >>= 1;            /* nr of 32-bit words.. */
  60                if (count) {
  61                        unsigned int carry = 0;
  62                        do {
  63                                unsigned int w = *(unsigned int *) buff;
  64                                count--;
  65                                buff += 4;
  66                                result += carry;
  67                                result += w;
  68                                carry = (w > result);
  69                        } while (count);
  70                        result += carry;
  71                        result = (result & 0xffff) + (result >> 16);
  72                }
  73                if (len & 2) {
  74                        result += *(unsigned short *) buff;
  75                        buff += 2;
  76                }
  77        }
  78        if (len & 1)
  79                result += le16_to_cpu(*buff);
  80        result = from32to16(result);
  81        if (odd)
  82                result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
  83out:
  84        return result;
  85}
  86
  87/*
  88 *      This is a version of ip_compute_csum() optimized for IP headers,
  89 *      which always checksum on 4 octet boundaries.
  90 */
  91__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
  92{
  93        return (__force __sum16)~do_csum(iph,ihl*4);
  94}
  95
  96/*
  97 * this routine is used for miscellaneous IP-like checksums, mainly
  98 * in icmp.c
  99 */
 100__sum16 ip_compute_csum(const void *buff, int len)
 101{
 102        return (__force __sum16)~do_csum(buff,len);
 103}
 104
 105/*
 106 * computes a partial checksum, e.g. for TCP/UDP fragments
 107 */
 108__wsum csum_partial(const void *buff, int len, __wsum sum)
 109{
 110        unsigned int result = do_csum(buff, len);
 111
 112        /* add in old sum, and carry.. */
 113        result += (__force u32)sum;
 114        if ((__force u32)sum > result)
 115                result += 1;
 116        return (__force __wsum)result;
 117}
 118
 119EXPORT_SYMBOL(csum_partial);
 120
 121/*
 122 * copy while checksumming, otherwise like csum_partial
 123 */
 124__wsum csum_partial_copy_nocheck(const void *src, void *dst,
 125                               int len, __wsum sum)
 126{
 127        /*
 128         * It's 2:30 am and I don't feel like doing it real ...
 129         * This is lots slower than the real thing (tm)
 130         */
 131        sum = csum_partial(src, len, sum);
 132        memcpy(dst, src, len);
 133
 134        return sum;
 135}
 136
 137/*
 138 * Copy from userspace and compute checksum.  If we catch an exception
 139 * then zero the rest of the buffer.
 140 */
 141__wsum csum_partial_copy_from_user (const void *src,
 142                                          void *dst,
 143                                          int len, __wsum sum,
 144                                          int *err_ptr)
 145{
 146        int missing;
 147
 148        missing = copy_from_user(dst, src, len);
 149        if (missing) {
 150                memset(dst + len - missing, 0, missing);
 151                *err_ptr = -EFAULT;
 152        }
 153                
 154        return csum_partial(dst, len, sum);
 155}
 156