* Add support for SK98xx driver
[platform/kernel/u-boot.git] / drivers / sk98lin / skcsum.c
1 /******************************************************************************
2  *
3  * Name:        skcsum.c
4  * Project:     GEnesis, PCI Gigabit Ethernet Adapter
5  * Version:     $Revision: 1.10 $
6  * Date:        $Date: 2002/04/11 10:02:04 $
7  * Purpose:     Store/verify Internet checksum in send/receive packets.
8  *
9  ******************************************************************************/
10
11 /******************************************************************************
12  *
13  *      (C)Copyright 1998-2001 SysKonnect GmbH.
14  *
15  *      This program is free software; you can redistribute it and/or modify
16  *      it under the terms of the GNU General Public License as published by
17  *      the Free Software Foundation; either version 2 of the License, or
18  *      (at your option) any later version.
19  *
20  *      The information in this file is provided "AS IS" without warranty.
21  *
22  ******************************************************************************/
23
24 /******************************************************************************
25  *
26  * History:
27  *
28  *      $Log: skcsum.c,v $
29  *      Revision 1.10  2002/04/11 10:02:04  rwahl
30  *      Fix in SkCsGetSendInfo():
31  *      - function did not return ProtocolFlags in every case.
32  *      - pseudo header csum calculated wrong for big endian.
33  *      
34  *      Revision 1.9  2001/06/13 07:42:08  gklug
35  *      fix: NetNumber was wrong in CLEAR_STAT event
36  *      add: check for good NetNumber in Clear STAT
37  *      
38  *      Revision 1.8  2001/02/06 11:15:36  rassmann
39  *      Supporting two nets on dual-port adapters.
40  *      
41  *      Revision 1.7  2000/06/29 13:17:05  rassmann
42  *      Corrected reception of a packet with UDP checksum == 0 (which means there
43  *      is no UDP checksum).
44  *      
45  *      Revision 1.6  2000/02/21 12:35:10  cgoos
46  *      Fixed license header comment.
47  *      
48  *      Revision 1.5  2000/02/21 11:05:19  cgoos
49  *      Merged changes back to common source.
50  *      Fixed rx path for BIG ENDIAN architecture.
51  *      
52  *      Revision 1.1  1999/07/26 15:28:12  mkarl
53  *      added return SKCS_STATUS_IP_CSUM_ERROR_UDP and
54  *      SKCS_STATUS_IP_CSUM_ERROR_TCP to pass the NidsTester
55  *      changed from common source to windows specific source
56  *      therefore restarting with v1.0
57  *      
58  *      Revision 1.3  1999/05/10 08:39:33  mkarl
59  *      prevent overflows in SKCS_HTON16
60  *      fixed a bug in pseudo header checksum calculation
61  *      added some comments
62  *      
63  *      Revision 1.2  1998/10/22 11:53:28  swolf
64  *      Now using SK_DBG_MSG.
65  *      
66  *      Revision 1.1  1998/09/01 15:35:41  swolf
67  *      initial revision
68  *
69  *      13-May-1998 sw  Created.
70  *
71  ******************************************************************************/
72
73 #ifdef SK_USE_CSUM      /* Check if CSUM is to be used. */
74
75 #ifndef lint
76 static const char SysKonnectFileId[] = "@(#)"
77         "$Id: skcsum.c,v 1.10 2002/04/11 10:02:04 rwahl Exp $"
78         " (C) SysKonnect.";
79 #endif  /* !lint */
80
81 /******************************************************************************
82  *
83  * Description:
84  *
85  * This is the "GEnesis" common module "CSUM".
86  *
87  * This module contains the code necessary to calculate, store, and verify the
88  * Internet Checksum of IP, TCP, and UDP frames.
89  *
90  * "GEnesis" is an abbreviation of "Gigabit Ethernet Network System in Silicon"
91  * and is the code name of this SysKonnect project.
92  *
93  * Compilation Options:
94  *
95  *      SK_USE_CSUM - Define if CSUM is to be used. Otherwise, CSUM will be an
96  *      empty module.
97  *
98  *      SKCS_OVERWRITE_PROTO - Define to overwrite the default protocol id
99  *      definitions. In this case, all SKCS_PROTO_xxx definitions must be made
100  *      external.
101  *
102  *      SKCS_OVERWRITE_STATUS - Define to overwrite the default return status
103  *      definitions. In this case, all SKCS_STATUS_xxx definitions must be made
104  *      external.
105  *
106  * Include File Hierarchy:
107  *
108  *      "h/skdrv1st.h"
109  *      "h/skcsum.h"
110  *       "h/sktypes.h"
111  *       "h/skqueue.h"
112  *      "h/skdrv2nd.h"
113  *
114  ******************************************************************************/
115
116 #include "h/skdrv1st.h"
117 #include "h/skcsum.h"
118 #include "h/skdrv2nd.h"
119
120 /* defines ********************************************************************/
121
122 /* The size of an Ethernet MAC header. */
123 #define SKCS_ETHERNET_MAC_HEADER_SIZE                   (6+6+2)
124
125 /* The size of the used topology's MAC header. */
126 #define SKCS_MAC_HEADER_SIZE    SKCS_ETHERNET_MAC_HEADER_SIZE
127
128 /* The size of the IP header without any option fields. */
129 #define SKCS_IP_HEADER_SIZE                                             20
130
131 /*
132  * Field offsets within the IP header.
133  */
134
135 /* "Internet Header Version" and "Length". */
136 #define SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH   0
137
138 /* "Total Length". */
139 #define SKCS_OFS_IP_TOTAL_LENGTH                                2
140
141 /* "Flags" "Fragment Offset". */
142 #define SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET   6
143
144 /* "Next Level Protocol" identifier. */
145 #define SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL                 9
146
147 /* Source IP address. */
148 #define SKCS_OFS_IP_SOURCE_ADDRESS                              12
149
150 /* Destination IP address. */
151 #define SKCS_OFS_IP_DESTINATION_ADDRESS                 16
152
153
154 /*
155  * Field offsets within the UDP header.
156  */
157
158 /* UDP checksum. */
159 #define SKCS_OFS_UDP_CHECKSUM                                   6
160
161 /* IP "Next Level Protocol" identifiers (see RFC 790). */
162 #define SKCS_PROTO_ID_TCP               6       /* Transport Control Protocol */
163 #define SKCS_PROTO_ID_UDP               17      /* User Datagram Protocol */
164
165 /* IP "Don't Fragment" bit. */
166 #define SKCS_IP_DONT_FRAGMENT   SKCS_HTON16(0x4000)
167
168 /* Add a byte offset to a pointer. */
169 #define SKCS_IDX(pPtr, Ofs)     ((void *) ((char *) (pPtr) + (Ofs)))
170
171 /*
172  * Macros that convert host to network representation and vice versa, i.e.
173  * little/big endian conversion on little endian machines only.
174  */
175 #ifdef SK_LITTLE_ENDIAN
176 #define SKCS_HTON16(Val16)      (((unsigned) (Val16) >> 8) | (((Val16) & 0xFF) << 8))
177 #endif  /* SK_LITTLE_ENDIAN */
178 #ifdef SK_BIG_ENDIAN
179 #define SKCS_HTON16(Val16)      (Val16)
180 #endif  /* SK_BIG_ENDIAN */
181 #define SKCS_NTOH16(Val16)      SKCS_HTON16(Val16)
182
183 /* typedefs *******************************************************************/
184
185 /* function prototypes ********************************************************/
186
187 /******************************************************************************
188  *
189  *      SkCsGetSendInfo - get checksum information for a send packet
190  *
191  * Description:
192  *      Get all checksum information necessary to send a TCP or UDP packet. The
193  *      function checks the IP header passed to it. If the high-level protocol
194  *      is either TCP or UDP the pseudo header checksum is calculated and
195  *      returned.
196  *
197  *      The function returns the total length of the IP header (including any
198  *      IP option fields), which is the same as the start offset of the IP data
199  *      which in turn is the start offset of the TCP or UDP header.
200  *
201  *      The function also returns the TCP or UDP pseudo header checksum, which
202  *      should be used as the start value for the hardware checksum calculation.
203  *      (Note that any actual pseudo header checksum can never calculate to
204  *      zero.)
205  *
206  * Note:
207  *      There is a bug in the ASIC which may lead to wrong checksums.
208  *
209  * Arguments:
210  *      pAc - A pointer to the adapter context struct.
211  *
212  *      pIpHeader - Pointer to IP header. Must be at least the IP header *not*
213  *      including any option fields, i.e. at least 20 bytes.
214  *
215  *      Note: This pointer will be used to address 8-, 16-, and 32-bit
216  *      variables with the respective alignment offsets relative to the pointer.
217  *      Thus, the pointer should point to a 32-bit aligned address. If the
218  *      target system cannot address 32-bit variables on non 32-bit aligned
219  *      addresses, then the pointer *must* point to a 32-bit aligned address.
220  *
221  *      pPacketInfo - A pointer to the packet information structure for this
222  *      packet. Before calling this SkCsGetSendInfo(), the following field must
223  *      be initialized:
224  *
225  *              ProtocolFlags - Initialize with any combination of
226  *              SKCS_PROTO_XXX bit flags. SkCsGetSendInfo() will only work on
227  *              the protocols specified here. Any protocol(s) not specified
228  *              here will be ignored.
229  *
230  *              Note: Only one checksum can be calculated in hardware. Thus, if
231  *              SKCS_PROTO_IP is specified in the 'ProtocolFlags',
232  *              SkCsGetSendInfo() must calculate the IP header checksum in
233  *              software. It might be a better idea to have the calling
234  *              protocol stack calculate the IP header checksum.
235  *
236  * Returns: N/A
237  *      On return, the following fields in 'pPacketInfo' may or may not have
238  *      been filled with information, depending on the protocol(s) found in the
239  *      packet:
240  *
241  *      ProtocolFlags - Returns the SKCS_PROTO_XXX bit flags of the protocol(s)
242  *      that were both requested by the caller and actually found in the packet.
243  *      Protocol(s) not specified by the caller and/or not found in the packet
244  *      will have their respective SKCS_PROTO_XXX bit flags reset.
245  *
246  *      Note: For IP fragments, TCP and UDP packet information is ignored.
247  *
248  *      IpHeaderLength - The total length in bytes of the complete IP header
249  *      including any option fields is returned here. This is the start offset
250  *      of the IP data, i.e. the TCP or UDP header if present.
251  *
252  *      IpHeaderChecksum - If IP has been specified in the 'ProtocolFlags', the
253  *      16-bit Internet Checksum of the IP header is returned here. This value
254  *      is to be stored into the packet's 'IP Header Checksum' field.
255  *
256  *      PseudoHeaderChecksum - If this is a TCP or UDP packet and if TCP or UDP
257  *      has been specified in the 'ProtocolFlags', the 16-bit Internet Checksum
258  *      of the TCP or UDP pseudo header is returned here.
259  */
260 #if 0
261 void SkCsGetSendInfo(
262 SK_AC                           *pAc,                   /* Adapter context struct. */
263 void                            *pIpHeader,             /* IP header. */
264 SKCS_PACKET_INFO        *pPacketInfo,   /* Packet information struct. */
265 int                                     NetNumber)              /* Net number */
266 {
267         /* Internet Header Version found in IP header. */
268         unsigned InternetHeaderVersion;
269
270         /* Length of the IP header as found in IP header. */
271         unsigned IpHeaderLength;
272
273         /* Bit field specifiying the desired/found protocols. */
274         unsigned ProtocolFlags;
275
276         /* Next level protocol identifier found in IP header. */
277         unsigned NextLevelProtocol;
278
279         /* Length of IP data portion. */
280         unsigned IpDataLength;
281
282         /* TCP/UDP pseudo header checksum. */
283         unsigned long PseudoHeaderChecksum;
284
285         /* Pointer to next level protocol statistics structure. */
286         SKCS_PROTO_STATS *NextLevelProtoStats;
287
288         /* Temporary variable. */
289         unsigned Tmp;
290
291         Tmp = *(SK_U8 *)
292                 SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH);
293
294         /* Get the Internet Header Version (IHV). */
295         /* Note: The IHV is stored in the upper four bits. */
296
297         InternetHeaderVersion = Tmp >> 4;
298
299         /* Check the Internet Header Version. */
300         /* Note: We currently only support IP version 4. */
301
302         if (InternetHeaderVersion != 4) {       /* IPv4? */
303                 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX,
304                         ("Tx: Unknown Internet Header Version %u.\n",
305                         InternetHeaderVersion));
306                 pPacketInfo->ProtocolFlags = 0;
307                 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++;
308                 return;
309         }
310
311         /* Get the IP header length (IHL). */
312         /*
313          * Note: The IHL is stored in the lower four bits as the number of
314          * 4-byte words.
315          */
316
317         IpHeaderLength = (Tmp & 0xf) * 4;
318         pPacketInfo->IpHeaderLength = IpHeaderLength;
319
320         /* Check the IP header length. */
321
322         /* 04-Aug-1998 sw - Really check the IHL? Necessary? */
323
324         if (IpHeaderLength < 5*4) {
325                 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX,
326                         ("Tx: Invalid IP Header Length %u.\n", IpHeaderLength));
327                 pPacketInfo->ProtocolFlags = 0;
328                 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxUnableCts++;
329                 return;
330         }
331
332         /* This is an IPv4 frame with a header of valid length. */
333
334         pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].TxOkCts++;
335
336         /* Check if we should calculate the IP header checksum. */
337
338         ProtocolFlags = pPacketInfo->ProtocolFlags;
339
340         if (ProtocolFlags & SKCS_PROTO_IP) {
341                 pPacketInfo->IpHeaderChecksum =
342                         SkCsCalculateChecksum(pIpHeader, IpHeaderLength);
343         }
344
345         /* Get the next level protocol identifier. */
346
347         NextLevelProtocol =
348                 *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL);
349
350         /*
351          * Check if this is a TCP or UDP frame and if we should calculate the
352          * TCP/UDP pseudo header checksum.
353          *
354          * Also clear all protocol bit flags of protocols not present in the
355          * frame.
356          */
357
358         if ((ProtocolFlags & SKCS_PROTO_TCP) != 0 &&
359                 NextLevelProtocol == SKCS_PROTO_ID_TCP) {
360                 /* TCP/IP frame. */
361                 ProtocolFlags &= SKCS_PROTO_TCP | SKCS_PROTO_IP;
362                 NextLevelProtoStats =
363                         &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP];
364         }
365         else if ((ProtocolFlags & SKCS_PROTO_UDP) != 0 &&
366                 NextLevelProtocol == SKCS_PROTO_ID_UDP) {
367                 /* UDP/IP frame. */
368                 ProtocolFlags &= SKCS_PROTO_UDP | SKCS_PROTO_IP;
369                 NextLevelProtoStats =
370                         &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP];
371         }
372         else {
373                 /*
374                  * Either not a TCP or UDP frame and/or TCP/UDP processing not
375                  * specified.
376                  */
377                 pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP;
378                 return;
379         }
380
381         /* Check if this is an IP fragment. */
382
383         /*
384          * Note: An IP fragment has a non-zero "Fragment Offset" field and/or
385          * the "More Fragments" bit set. Thus, if both the "Fragment Offset"
386          * and the "More Fragments" are zero, it is *not* a fragment. We can
387          * easily check both at the same time since they are in the same 16-bit
388          * word.
389          */
390
391         if ((*(SK_U16 *)
392                 SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) &
393                 ~SKCS_IP_DONT_FRAGMENT) != 0) {
394                 /* IP fragment; ignore all other protocols. */
395                 pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP;
396                 NextLevelProtoStats->TxUnableCts++;
397                 return;
398         }
399
400         /*
401          * Calculate the TCP/UDP pseudo header checksum.
402          */
403
404         /* Get total length of IP header and data. */
405
406         IpDataLength =
407                 *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH);
408
409         /* Get length of IP data portion. */
410
411         IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength;
412
413         /* Calculate the sum of all pseudo header fields (16-bit). */
414
415         PseudoHeaderChecksum =
416                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
417                         SKCS_OFS_IP_SOURCE_ADDRESS + 0) +
418                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
419                         SKCS_OFS_IP_SOURCE_ADDRESS + 2) +
420                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
421                         SKCS_OFS_IP_DESTINATION_ADDRESS + 0) +
422                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
423                         SKCS_OFS_IP_DESTINATION_ADDRESS + 2) +
424                 (unsigned long) SKCS_HTON16(NextLevelProtocol) +
425                 (unsigned long) SKCS_HTON16(IpDataLength);
426         
427         /* Add-in any carries. */
428
429         SKCS_OC_ADD(PseudoHeaderChecksum, PseudoHeaderChecksum, 0);
430
431         /* Add-in any new carry. */
432
433         SKCS_OC_ADD(pPacketInfo->PseudoHeaderChecksum, PseudoHeaderChecksum, 0);
434
435         pPacketInfo->ProtocolFlags = ProtocolFlags;
436         NextLevelProtoStats->TxOkCts++; /* Success. */
437 }       /* SkCsGetSendInfo */
438
439
440 /******************************************************************************
441  *
442  *      SkCsGetReceiveInfo - verify checksum information for a received packet
443  *
444  * Description:
445  *      Verify a received frame's checksum. The function returns a status code
446  *      reflecting the result of the verification.
447  *
448  * Note:
449  *      Before calling this function you have to verify that the frame is
450  *      not padded and Checksum1 and Checksum2 are bigger than 1.
451  *
452  * Arguments:
453  *      pAc - Pointer to adapter context struct.
454  *
455  *      pIpHeader - Pointer to IP header. Must be at least the length in bytes
456  *      of the received IP header including any option fields. For UDP packets,
457  *      8 additional bytes are needed to access the UDP checksum.
458  *
459  *      Note: The actual length of the IP header is stored in the lower four
460  *      bits of the first octet of the IP header as the number of 4-byte words,
461  *      so it must be multiplied by four to get the length in bytes. Thus, the
462  *      maximum IP header length is 15 * 4 = 60 bytes.
463  *
464  *      Checksum1 - The first 16-bit Internet Checksum calculated by the
465  *      hardware starting at the offset returned by SkCsSetReceiveFlags().
466  *
467  *      Checksum2 - The second 16-bit Internet Checksum calculated by the
468  *      hardware starting at the offset returned by SkCsSetReceiveFlags().
469  *
470  * Returns:
471  *      SKCS_STATUS_UNKNOWN_IP_VERSION - Not an IP v4 frame.
472  *      SKCS_STATUS_IP_CSUM_ERROR - IP checksum error.
473  *      SKCS_STATUS_IP_CSUM_ERROR_TCP - IP checksum error in TCP frame.
474  *      SKCS_STATUS_IP_CSUM_ERROR_UDP - IP checksum error in UDP frame
475  *      SKCS_STATUS_IP_FRAGMENT - IP fragment (IP checksum ok).
476  *      SKCS_STATUS_IP_CSUM_OK - IP checksum ok (not a TCP or UDP frame).
477  *      SKCS_STATUS_TCP_CSUM_ERROR - TCP checksum error (IP checksum ok).
478  *      SKCS_STATUS_UDP_CSUM_ERROR - UDP checksum error (IP checksum ok).
479  *      SKCS_STATUS_TCP_CSUM_OK - IP and TCP checksum ok.
480  *      SKCS_STATUS_UDP_CSUM_OK - IP and UDP checksum ok.
481  *      SKCS_STATUS_IP_CSUM_OK_NO_UDP - IP checksum OK and no UDP checksum.
482  *
483  *      Note: If SKCS_OVERWRITE_STATUS is defined, the SKCS_STATUS_XXX values
484  *      returned here can be defined in some header file by the module using CSUM.
485  *      In this way, the calling module can assign return values for its own needs,
486  *      e.g. by assigning bit flags to the individual protocols.
487  */
488 SKCS_STATUS SkCsGetReceiveInfo(
489 SK_AC           *pAc,           /* Adapter context struct. */
490 void            *pIpHeader,     /* IP header. */
491 unsigned        Checksum1,      /* Hardware checksum 1. */
492 unsigned        Checksum2,      /* Hardware checksum 2. */
493 int                     NetNumber)      /* Net number */
494 {
495         /* Internet Header Version found in IP header. */
496         unsigned InternetHeaderVersion;
497
498         /* Length of the IP header as found in IP header. */
499         unsigned IpHeaderLength;
500
501         /* Length of IP data portion. */
502         unsigned IpDataLength;
503
504         /* IP header checksum. */
505         unsigned IpHeaderChecksum;
506
507         /* IP header options checksum, if any. */
508         unsigned IpOptionsChecksum;
509
510         /* IP data checksum, i.e. TCP/UDP checksum. */
511         unsigned IpDataChecksum;
512
513         /* Next level protocol identifier found in IP header. */
514         unsigned NextLevelProtocol;
515
516         /* The checksum of the "next level protocol", i.e. TCP or UDP. */
517         unsigned long NextLevelProtocolChecksum;
518
519         /* Pointer to next level protocol statistics structure. */
520         SKCS_PROTO_STATS *NextLevelProtoStats;
521
522         /* Temporary variable. */
523         unsigned Tmp;
524
525         Tmp = *(SK_U8 *)
526                 SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH);
527
528         /* Get the Internet Header Version (IHV). */
529         /* Note: The IHV is stored in the upper four bits. */
530
531         InternetHeaderVersion = Tmp >> 4;
532
533         /* Check the Internet Header Version. */
534         /* Note: We currently only support IP version 4. */
535
536         if (InternetHeaderVersion != 4) {       /* IPv4? */
537                 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX,
538                         ("Rx: Unknown Internet Header Version %u.\n",
539                         InternetHeaderVersion));
540                 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxUnableCts++;
541                 return (SKCS_STATUS_UNKNOWN_IP_VERSION);
542         }
543
544         /* Get the IP header length (IHL). */
545         /*
546          * Note: The IHL is stored in the lower four bits as the number of
547          * 4-byte words.
548          */
549
550         IpHeaderLength = (Tmp & 0xf) * 4;
551
552         /* Check the IP header length. */
553
554         /* 04-Aug-1998 sw - Really check the IHL? Necessary? */
555
556         if (IpHeaderLength < 5*4) {
557                 SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX,
558                         ("Rx: Invalid IP Header Length %u.\n", IpHeaderLength));
559                 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++;
560                 return (SKCS_STATUS_IP_CSUM_ERROR);
561         }
562
563         /* This is an IPv4 frame with a header of valid length. */
564
565         /* Get the IP header and data checksum. */
566
567         IpDataChecksum = Checksum2;
568
569         /*
570          * The IP header checksum is calculated as follows:
571          *
572          *      IpHeaderChecksum = Checksum1 - Checksum2
573          */
574
575         SKCS_OC_SUB(IpHeaderChecksum, Checksum1, Checksum2);
576
577         /* Check if any IP header options. */
578
579         if (IpHeaderLength > SKCS_IP_HEADER_SIZE) {
580
581                 /* Get the IP options checksum. */
582
583                 IpOptionsChecksum = SkCsCalculateChecksum(
584                         SKCS_IDX(pIpHeader, SKCS_IP_HEADER_SIZE),
585                         IpHeaderLength - SKCS_IP_HEADER_SIZE);
586
587                 /* Adjust the IP header and IP data checksums. */
588
589                 SKCS_OC_ADD(IpHeaderChecksum, IpHeaderChecksum, IpOptionsChecksum);
590
591                 SKCS_OC_SUB(IpDataChecksum, IpDataChecksum, IpOptionsChecksum);
592         }
593
594         /*
595          * Check if the IP header checksum is ok.
596          *
597          * NOTE: We must check the IP header checksum even if the caller just wants
598          * us to check upper-layer checksums, because we cannot do any further
599          * processing of the packet without a valid IP checksum.
600          */
601         
602         /* Get the next level protocol identifier. */
603         
604         NextLevelProtocol = *(SK_U8 *)
605                 SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL);
606
607         if (IpHeaderChecksum != 0xFFFF) {
608                 pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_IP].RxErrCts++;
609                 /* the NDIS tester wants to know the upper level protocol too */
610                 if (NextLevelProtocol == SKCS_PROTO_ID_TCP) {
611                         return(SKCS_STATUS_IP_CSUM_ERROR_TCP);
612                 }
613                 else if (NextLevelProtocol == SKCS_PROTO_ID_UDP) {
614                         return(SKCS_STATUS_IP_CSUM_ERROR_UDP);
615                 }
616                 return (SKCS_STATUS_IP_CSUM_ERROR);
617         }
618
619         /*
620          * Check if this is a TCP or UDP frame and if we should calculate the
621          * TCP/UDP pseudo header checksum.
622          *
623          * Also clear all protocol bit flags of protocols not present in the
624          * frame.
625          */
626
627         if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_TCP) != 0 &&
628                 NextLevelProtocol == SKCS_PROTO_ID_TCP) {
629                 /* TCP/IP frame. */
630                 NextLevelProtoStats =
631                         &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_TCP];
632         }
633         else if ((pAc->Csum.ReceiveFlags[NetNumber] & SKCS_PROTO_UDP) != 0 &&
634                 NextLevelProtocol == SKCS_PROTO_ID_UDP) {
635                 /* UDP/IP frame. */
636                 NextLevelProtoStats =
637                         &pAc->Csum.ProtoStats[NetNumber][SKCS_PROTO_STATS_UDP];
638         }
639         else {
640                 /*
641                  * Either not a TCP or UDP frame and/or TCP/UDP processing not
642                  * specified.
643                  */
644                 return (SKCS_STATUS_IP_CSUM_OK);
645         }
646
647         /* Check if this is an IP fragment. */
648
649         /*
650          * Note: An IP fragment has a non-zero "Fragment Offset" field and/or
651          * the "More Fragments" bit set. Thus, if both the "Fragment Offset"
652          * and the "More Fragments" are zero, it is *not* a fragment. We can
653          * easily check both at the same time since they are in the same 16-bit
654          * word.
655          */
656
657         if ((*(SK_U16 *)
658                 SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) &
659                 ~SKCS_IP_DONT_FRAGMENT) != 0) {
660                 /* IP fragment; ignore all other protocols. */
661                 NextLevelProtoStats->RxUnableCts++;
662                 return (SKCS_STATUS_IP_FRAGMENT);
663         }
664
665         /*
666          * 08-May-2000 ra
667          *
668          * From RFC 768 (UDP)
669          * If the computed checksum is zero, it is transmitted as all ones (the
670          * equivalent in one's complement arithmetic).  An all zero transmitted
671          * checksum value means that the transmitter generated no checksum (for
672          * debugging or for higher level protocols that don't care).
673          */
674
675         if (NextLevelProtocol == SKCS_PROTO_ID_UDP &&
676                 *(SK_U16*)SKCS_IDX(pIpHeader, IpHeaderLength + 6) == 0x0000) {
677
678                 NextLevelProtoStats->RxOkCts++;
679                 
680                 return (SKCS_STATUS_IP_CSUM_OK_NO_UDP);
681         }
682
683         /*
684          * Calculate the TCP/UDP checksum.
685          */
686
687         /* Get total length of IP header and data. */
688
689         IpDataLength =
690                 *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH);
691
692         /* Get length of IP data portion. */
693
694         IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength;
695
696         NextLevelProtocolChecksum =
697
698                 /* Calculate the pseudo header checksum. */
699
700                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
701                         SKCS_OFS_IP_SOURCE_ADDRESS + 0) +
702                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
703                         SKCS_OFS_IP_SOURCE_ADDRESS + 2) +
704                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
705                         SKCS_OFS_IP_DESTINATION_ADDRESS + 0) +
706                 (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader,
707                         SKCS_OFS_IP_DESTINATION_ADDRESS + 2) +
708                 (unsigned long) SKCS_HTON16(NextLevelProtocol) +
709                 (unsigned long) SKCS_HTON16(IpDataLength) +
710
711                 /* Add the TCP/UDP header checksum. */
712
713                 (unsigned long) IpDataChecksum;
714
715         /* Add-in any carries. */
716
717         SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0);
718
719         /* Add-in any new carry. */
720
721         SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0);
722
723         /* Check if the TCP/UDP checksum is ok. */
724
725         if ((unsigned) NextLevelProtocolChecksum == 0xFFFF) {
726
727                 /* TCP/UDP checksum ok. */
728
729                 NextLevelProtoStats->RxOkCts++;
730
731                 return (NextLevelProtocol == SKCS_PROTO_ID_TCP ?
732                         SKCS_STATUS_TCP_CSUM_OK : SKCS_STATUS_UDP_CSUM_OK);
733         }
734         
735         /* TCP/UDP checksum error. */
736
737         NextLevelProtoStats->RxErrCts++;
738
739         return (NextLevelProtocol == SKCS_PROTO_ID_TCP ?
740                 SKCS_STATUS_TCP_CSUM_ERROR : SKCS_STATUS_UDP_CSUM_ERROR);
741 }       /* SkCsGetReceiveInfo */
742 #endif
743
744
745 /******************************************************************************
746  *
747  *      SkCsSetReceiveFlags - set checksum receive flags
748  *
749  * Description:
750  *      Use this function to set the various receive flags. According to the
751  *      protocol flags set by the caller, the start offsets within received
752  *      packets of the two hardware checksums are returned. These offsets must
753  *      be stored in all receive descriptors.
754  *
755  * Arguments:
756  *      pAc - Pointer to adapter context struct.
757  *
758  *      ReceiveFlags - Any combination of SK_PROTO_XXX flags of the protocols
759  *      for which the caller wants checksum information on received frames.
760  *
761  *      pChecksum1Offset - The start offset of the first receive descriptor
762  *      hardware checksum to be calculated for received frames is returned
763  *      here.
764  *
765  *      pChecksum2Offset - The start offset of the second receive descriptor
766  *      hardware checksum to be calculated for received frames is returned
767  *      here.
768  *
769  * Returns: N/A
770  *      Returns the two hardware checksum start offsets.
771  */
772 void SkCsSetReceiveFlags(
773 SK_AC           *pAc,                           /* Adapter context struct. */
774 unsigned        ReceiveFlags,           /* New receive flags. */
775 unsigned        *pChecksum1Offset,      /* Offset for hardware checksum 1. */
776 unsigned        *pChecksum2Offset,      /* Offset for hardware checksum 2. */
777 int                     NetNumber)
778 {
779         /* Save the receive flags. */
780
781         pAc->Csum.ReceiveFlags[NetNumber] = ReceiveFlags;
782
783         /* First checksum start offset is the IP header. */
784         *pChecksum1Offset = SKCS_MAC_HEADER_SIZE;
785
786         /*
787          * Second checksum start offset is the IP data. Note that this may vary
788          * if there are any IP header options in the actual packet.
789          */
790         *pChecksum2Offset = SKCS_MAC_HEADER_SIZE + SKCS_IP_HEADER_SIZE;
791 }       /* SkCsSetReceiveFlags */
792
793 #ifndef SkCsCalculateChecksum
794
795 /******************************************************************************
796  *
797  *      SkCsCalculateChecksum - calculate checksum for specified data
798  *
799  * Description:
800  *      Calculate and return the 16-bit Internet Checksum for the specified
801  *      data.
802  *
803  * Arguments:
804  *      pData - Pointer to data for which the checksum shall be calculated.
805  *      Note: The pointer should be aligned on a 16-bit boundary.
806  *
807  *      Length - Length in bytes of data to checksum.
808  *
809  * Returns:
810  *      The 16-bit Internet Checksum for the specified data.
811  *
812  *      Note: The checksum is calculated in the machine's natural byte order,
813  *      i.e. little vs. big endian. Thus, the resulting checksum is different
814  *      for the same input data on little and big endian machines.
815  *
816  *      However, when written back to the network packet, the byte order is
817  *      always in correct network order.
818  */
819 unsigned SkCsCalculateChecksum(
820 void            *pData,         /* Data to checksum. */
821 unsigned        Length)         /* Length of data. */
822 {
823         SK_U16 *pU16;           /* Pointer to the data as 16-bit words. */
824         unsigned long Checksum; /* Checksum; must be at least 32 bits. */
825
826         /* Sum up all 16-bit words. */
827
828         pU16 = (SK_U16 *) pData;
829         for (Checksum = 0; Length > 1; Length -= 2) {
830                 Checksum += *pU16++;
831         }
832
833         /* If this is an odd number of bytes, add-in the last byte. */
834
835         if (Length > 0) {
836 #ifdef SK_BIG_ENDIAN
837                 /* Add the last byte as the high byte. */
838                 Checksum += ((unsigned) *(SK_U8 *) pU16) << 8;
839 #else   /* !SK_BIG_ENDIAN */
840                 /* Add the last byte as the low byte. */
841                 Checksum += *(SK_U8 *) pU16;
842 #endif  /* !SK_BIG_ENDIAN */
843         }
844
845         /* Add-in any carries. */
846
847         SKCS_OC_ADD(Checksum, Checksum, 0);
848
849         /* Add-in any new carry. */
850
851         SKCS_OC_ADD(Checksum, Checksum, 0);
852
853         /* Note: All bits beyond the 16-bit limit are now zero. */
854
855         return ((unsigned) Checksum);
856 }       /* SkCsCalculateChecksum */
857
858 #endif /* SkCsCalculateChecksum */
859
860 /******************************************************************************
861  *
862  *      SkCsEvent - the CSUM event dispatcher
863  *
864  * Description:
865  *      This is the event handler for the CSUM module.
866  *
867  * Arguments:
868  *      pAc - Pointer to adapter context.
869  *
870  *      Ioc - I/O context.
871  *
872  *      Event -  Event id.
873  *
874  *      Param - Event dependent parameter.
875  *
876  * Returns:
877  *      The 16-bit Internet Checksum for the specified data.
878  *
879  *      Note: The checksum is calculated in the machine's natural byte order,
880  *      i.e. little vs. big endian. Thus, the resulting checksum is different
881  *      for the same input data on little and big endian machines.
882  *
883  *      However, when written back to the network packet, the byte order is
884  *      always in correct network order.
885  */
886 int SkCsEvent(
887 SK_AC           *pAc,   /* Pointer to adapter context. */
888 SK_IOC          Ioc,    /* I/O context. */
889 SK_U32          Event,  /* Event id. */
890 SK_EVPARA       Param)  /* Event dependent parameter. */
891 {
892         int ProtoIndex;
893         int     NetNumber;
894
895         switch (Event) {
896         /*
897          * Clear protocol statistics.
898          *
899          * Param - Protocol index, or -1 for all protocols.
900          *               - Net number.
901          */
902         case SK_CSUM_EVENT_CLEAR_PROTO_STATS:
903
904                 ProtoIndex = (int)Param.Para32[1];
905                 NetNumber = (int)Param.Para32[0];
906                 if (ProtoIndex < 0) {   /* Clear for all protocols. */
907                         if (NetNumber >= 0) {
908                                 memset(&pAc->Csum.ProtoStats[NetNumber][0], 0,
909                                         sizeof(pAc->Csum.ProtoStats[NetNumber]));
910                         }
911                 }
912                 else {                                  /* Clear for individual protocol. */
913                         memset(&pAc->Csum.ProtoStats[NetNumber][ProtoIndex], 0,
914                                 sizeof(pAc->Csum.ProtoStats[NetNumber][ProtoIndex]));
915                 }
916                 break;
917         default:
918                 break;
919         }
920         return (0);     /* Success. */
921 }       /* SkCsEvent */
922
923 #endif  /* SK_USE_CSUM */