2005-04-17 02:20:36 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 1995 , 96 , 97 , 98 , 99 , 2001 by Ralf Baechle
* Copyright ( C ) 1999 Silicon Graphics , Inc .
* Copyright ( C ) 2001 Thiemo Seufer .
* Copyright ( C ) 2002 Maciej W . Rozycki
*/
# ifndef _ASM_CHECKSUM_H
# define _ASM_CHECKSUM_H
# include <linux/config.h>
# include <linux/in6.h>
# include <asm/uaccess.h>
/*
* computes the checksum of a memory block at buff , length len ,
* and adds in " sum " ( 32 - bit )
*
* returns a 32 - bit number suitable for feeding into itself
* or csum_tcpudp_magic
*
* this function must be called with even lengths , except
* for the last fragment , which may be odd
*
* it ' s best to have buff aligned on a 32 - bit boundary
*/
unsigned int csum_partial ( const unsigned char * buff , int len , unsigned int sum ) ;
/*
* this is a new version of the above that records errors it finds in * errp ,
* but continues and zeros the rest of the buffer .
*/
2005-03-01 22:22:29 +03:00
unsigned int csum_partial_copy_from_user ( const unsigned char __user * src ,
unsigned char * dst , int len ,
unsigned int sum , int * errp ) ;
2005-04-17 02:20:36 +04:00
/*
* Copy and checksum to user
*/
# define HAVE_CSUM_COPY_USER
static inline unsigned int csum_and_copy_to_user ( const unsigned char * src ,
unsigned char __user * dst ,
int len , int sum ,
int * err_ptr )
{
might_sleep ( ) ;
sum = csum_partial ( src , len , sum ) ;
if ( copy_to_user ( dst , src , len ) ) {
* err_ptr = - EFAULT ;
return - 1 ;
}
return sum ;
}
/*
* the same as csum_partial , but copies from user space ( but on MIPS
* we have just one address space , so this is identical to the above )
*/
unsigned int csum_partial_copy_nocheck ( const unsigned char * src , unsigned char * dst ,
int len , unsigned int sum ) ;
/*
* Fold a partial checksum without adding pseudo headers
*/
static inline unsigned short int csum_fold ( unsigned int sum )
{
__asm__ (
" .set \t noat \t \t \t # csum_fold \n \t "
" sll \t $1,%0,16 \n \t "
" addu \t %0,$1 \n \t "
" sltu \t $1,%0,$1 \n \t "
" srl \t %0,%0,16 \n \t "
" addu \t %0,$1 \n \t "
" xori \t %0,0xffff \n \t "
" .set \t at "
: " =r " ( sum )
: " 0 " ( sum ) ) ;
return sum ;
}
/*
* This is a version of ip_compute_csum ( ) optimized for IP headers ,
* which always checksum on 4 octet boundaries .
*
* By Jorge Cwik < jorge @ laser . satlink . net > , adapted for linux by
* Arnt Gulbrandsen .
*/
static inline unsigned short ip_fast_csum ( unsigned char * iph , unsigned int ihl )
{
unsigned int * word = ( unsigned int * ) iph ;
unsigned int * stop = word + ihl ;
unsigned int csum ;
int carry ;
csum = word [ 0 ] ;
csum + = word [ 1 ] ;
carry = ( csum < word [ 1 ] ) ;
csum + = carry ;
csum + = word [ 2 ] ;
carry = ( csum < word [ 2 ] ) ;
csum + = carry ;
csum + = word [ 3 ] ;
carry = ( csum < word [ 3 ] ) ;
csum + = carry ;
word + = 4 ;
do {
csum + = * word ;
carry = ( csum < * word ) ;
csum + = carry ;
word + + ;
} while ( word ! = stop ) ;
return csum_fold ( csum ) ;
}
static inline unsigned int csum_tcpudp_nofold ( unsigned long saddr ,
unsigned long daddr , unsigned short len , unsigned short proto ,
unsigned int sum )
{
__asm__ (
" .set \t noat \t \t \t # csum_tcpudp_nofold \n \t "
2005-09-04 02:56:16 +04:00
# ifdef CONFIG_32BIT
2005-04-17 02:20:36 +04:00
" addu \t %0, %2 \n \t "
" sltu \t $1, %0, %2 \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %3 \n \t "
" sltu \t $1, %0, %3 \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %4 \n \t "
" sltu \t $1, %0, %4 \n \t "
" addu \t %0, $1 \n \t "
# endif
2005-09-04 02:56:16 +04:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
" daddu \t %0, %2 \n \t "
" daddu \t %0, %3 \n \t "
" daddu \t %0, %4 \n \t "
" dsll32 \t $1, %0, 0 \n \t "
" daddu \t %0, $1 \n \t "
" dsrl32 \t %0, %0, 0 \n \t "
# endif
" .set \t at "
: " =r " ( sum )
: " 0 " ( daddr ) , " r " ( saddr ) ,
# ifdef __MIPSEL__
" r " ( ( ( unsigned long ) htons ( len ) < < 16 ) + proto * 256 ) ,
# else
" r " ( ( ( unsigned long ) ( proto ) < < 16 ) + len ) ,
# endif
" r " ( sum ) ) ;
return sum ;
}
/*
* computes the checksum of the TCP / UDP pseudo - header
* returns a 16 - bit checksum , already complemented
*/
static inline unsigned short int csum_tcpudp_magic ( unsigned long saddr ,
unsigned long daddr ,
unsigned short len ,
unsigned short proto ,
unsigned int sum )
{
return csum_fold ( csum_tcpudp_nofold ( saddr , daddr , len , proto , sum ) ) ;
}
/*
* this routine is used for miscellaneous IP - like checksums , mainly
* in icmp . c
*/
static inline unsigned short ip_compute_csum ( unsigned char * buff , int len )
{
return csum_fold ( csum_partial ( buff , len , 0 ) ) ;
}
# define _HAVE_ARCH_IPV6_CSUM
static __inline__ unsigned short int csum_ipv6_magic ( struct in6_addr * saddr ,
struct in6_addr * daddr ,
__u32 len ,
unsigned short proto ,
unsigned int sum )
{
__asm__ (
" .set \t push \t \t \t # csum_ipv6_magic \n \t "
" .set \t noreorder \n \t "
" .set \t noat \n \t "
" addu \t %0, %5 \t \t \t # proto (long in network byte order) \n \t "
" sltu \t $1, %0, %5 \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %6 \t \t \t # csum \n \t "
" sltu \t $1, %0, %6 \n \t "
" lw \t %1, 0(%2) \t \t \t # four words source address \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 4(%2) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 8(%2) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 12(%2) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 0(%3) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 4(%3) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 8(%3) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" lw \t %1, 12(%3) \n \t "
" addu \t %0, $1 \n \t "
" addu \t %0, %1 \n \t "
" sltu \t $1, %0, %1 \n \t "
" addu \t %0, $1 \t \t \t # Add final carry \n \t "
" .set \t pop "
: " =r " ( sum ) , " =r " ( proto )
: " r " ( saddr ) , " r " ( daddr ) ,
" 0 " ( htonl ( len ) ) , " 1 " ( htonl ( proto ) ) , " r " ( sum ) ) ;
return csum_fold ( sum ) ;
}
# endif /* _ASM_CHECKSUM_H */