linux/arch/parisc/lib/checksum.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * INET         An implementation of the TCP/IP protocol suite for the LINUX
   4 *              operating system.  INET is implemented using the  BSD Socket
   5 *              interface as the means of communication with the user level.
   6 *
   7 *              MIPS specific IP/TCP/UDP checksumming routines
   8 *
   9 * Authors:     Ralf Baechle, <ralf@waldorf-gmbh.de>
  10 *              Lots of code moved from tcp.c and ip.c; see those files
  11 *              for more names.
  12 */
  13#include <linux/module.h>
  14#include <linux/types.h>
  15
  16#include <net/checksum.h>
  17#include <asm/byteorder.h>
  18#include <asm/string.h>
  19#include <linux/uaccess.h>
  20
  21#define addc(_t,_r)                     \
  22        __asm__ __volatile__ (          \
  23"       add             %0, %1, %0\n"   \
  24"       addc            %0, %%r0, %0\n" \
  25        : "=r"(_t)                      \
  26        : "r"(_r), "0"(_t));
  27
  28static inline unsigned short from32to16(unsigned int x)
  29{
  30        /* 32 bits --> 16 bits + carry */
  31        x = (x & 0xffff) + (x >> 16);
  32        /* 16 bits + carry --> 16 bits including carry */
  33        x = (x & 0xffff) + (x >> 16);
  34        return (unsigned short)x;
  35}
  36
  37static inline unsigned int do_csum(const unsigned char * buff, int len)
  38{
  39        int odd, count;
  40        unsigned int result = 0;
  41
  42        if (len <= 0)
  43                goto out;
  44        odd = 1 & (unsigned long) buff;
  45        if (odd) {
  46                result = be16_to_cpu(*buff);
  47                len--;
  48                buff++;
  49        }
  50        count = len >> 1;               /* nr of 16-bit words.. */
  51        if (count) {
  52                if (2 & (unsigned long) buff) {
  53                        result += *(unsigned short *) buff;
  54                        count--;
  55                        len -= 2;
  56                        buff += 2;
  57                }
  58                count >>= 1;            /* nr of 32-bit words.. */
  59                if (count) {
  60                        while (count >= 4) {
  61                                unsigned int r1, r2, r3, r4;
  62                                r1 = *(unsigned int *)(buff + 0);
  63                                r2 = *(unsigned int *)(buff + 4);
  64                                r3 = *(unsigned int *)(buff + 8);
  65                                r4 = *(unsigned int *)(buff + 12);
  66                                addc(result, r1);
  67                                addc(result, r2);
  68                                addc(result, r3);
  69                                addc(result, r4);
  70                                count -= 4;
  71                                buff += 16;
  72                        }
  73                        while (count) {
  74                                unsigned int w = *(unsigned int *) buff;
  75                                count--;
  76                                buff += 4;
  77                                addc(result, w);
  78                        }
  79                        result = (result & 0xffff) + (result >> 16);
  80                }
  81                if (len & 2) {
  82                        result += *(unsigned short *) buff;
  83                        buff += 2;
  84                }
  85        }
  86        if (len & 1)
  87                result += le16_to_cpu(*buff);
  88        result = from32to16(result);
  89        if (odd)
  90                result = swab16(result);
  91out:
  92        return result;
  93}
  94
  95/*
  96 * computes a partial checksum, e.g. for TCP/UDP fragments
  97 */
  98/*
  99 * why bother folding?
 100 */
 101__wsum csum_partial(const void *buff, int len, __wsum sum)
 102{
 103        unsigned int result = do_csum(buff, len);
 104        addc(result, sum);
 105        return (__force __wsum)from32to16(result);
 106}
 107
 108EXPORT_SYMBOL(csum_partial);
 109