00001 // //////////////////////////////////////////////////////////////////////////// 00002 // //////////////////////////////////////////////////////////////////////////// 00031 /********************************************************************* 00032 * Software License Agreement 00033 * 00034 * Copyright (C) 2002-2008 Microchip Technology Inc. All rights 00035 * reserved. 00036 * 00037 * Microchip licenses to you the right to use, modify, copy, and 00038 * distribute: 00039 * (i) the Software when embedded on a Microchip microcontroller or 00040 * digital signal controller product ("Device") which is 00041 * integrated into Licensee's product; or 00042 * (ii) ONLY the Software driver source files ENC28J60.c and 00043 * ENC28J60.h ported to a non-Microchip device used in 00044 * conjunction with a Microchip ethernet controller for the 00045 * sole purpose of interfacing with the ethernet controller. 00046 * 00047 * You should refer to the license agreement accompanying this 00048 * Software for additional information regarding your rights and 00049 * obligations. 00050 * 00051 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 00052 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 00053 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 00054 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 00055 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 00056 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 00057 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 00058 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 00059 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 00060 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 00061 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE. 00062 * 00063 * 00064 /********************************************************************* 00065 * Author Date Comment 00066 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00067 * Rawin Rojvanit 07/26/05 Stuff 00068 * Howard Schlunder 11/17/05 Ported to PIC18F97J60 00069 * Howard Schlunder 06/16/06 Synchronized with ENC28J60 code 00070 * Howard Schlunder 05/21/07 Fixed a TX lockup problem 00071 ********************************************************************/ 00072 #define __ETH97J60_C 00073 00074 #include "TCPIP.h" 00075 00076 // MODIFIX: Removed the macro request for the ethernet transceiver. 00077 00078 00080 // Since the Ethernet PHY doesn't support auto-negotiation, full-duplex mode is 00081 // not compatible with most switches/routers. If a dedicated network is used 00082 // where the duplex of the remote node can be manually configured, you may 00083 // change this configuration. Otherwise, half duplex should always be used. 00084 #define HALF_DUPLEX 00085 //#define FULL_DUPLEX 00086 00087 // Pseudo Functions 00088 #define LOW(a) (a & 0xFF) 00089 #define HIGH(a) ((a>>8) & 0xFF) 00090 00091 #define ETHER_IP (0x00u) 00092 #define ETHER_ARP (0x06u) 00093 00094 // A header appended at the start of all RX frames by the hardware 00095 typedef struct _ENC_PREAMBLE 00096 { 00097 WORD NextPacketPointer; 00098 RXSTATUS StatusVector; 00099 00100 MAC_ADDR DestMACAddr; 00101 MAC_ADDR SourceMACAddr; 00102 WORD_VAL Type; 00103 } ENC_PREAMBLE; 00104 00105 00106 // Internal MAC level variables and flags. 00107 static WORD_VAL NextPacketLocation; 00108 static WORD_VAL CurrentPacketLocation; 00109 static BOOL WasDiscarded; 00110 static WORD wTXWatchdog; 00111 00112 00113 /****************************************************************************** 00114 * Function: void MACInit(void) 00115 * 00116 * PreCondition: None 00117 * 00118 * Input: None 00119 * 00120 * Output: None 00121 * 00122 * Side Effects: None 00123 * 00124 * Overview: MACInit enables the Ethernet module, waits for the 00125 * to become ready, and programs all registers for future 00126 * TX/RX operations. 00127 * 00128 * Note: This function blocks for at least 1ms, waiting for the 00129 * hardware to stabilize. 00130 *****************************************************************************/ 00131 void MACInit(void) 00132 { 00133 BYTE i; 00134 00135 LATAbits.LATA0 = 0; 00136 LATAbits.LATA1 = 0; 00137 00138 TRISAbits.TRISA0 = 0; // Set LED0 as output 00139 TRISAbits.TRISA1 = 0; // Set LED1 as output 00140 ECON2bits.ETHEN = 1; // Enable Ethernet! 00141 00142 // Wait for PHYRDY to become set. 00143 while(!ESTATbits.PHYRDY); 00144 00145 // Configure the receive buffer boundary pointers 00146 // and the buffer write protect pointer (receive buffer read pointer) 00147 WasDiscarded = TRUE; 00148 NextPacketLocation.Val = RXSTART; 00149 ERXST = RXSTART; 00150 ERXRDPTL = LOW(RXSTOP); // Write low byte first 00151 ERXRDPTH = HIGH(RXSTOP);// Write high byte last 00152 ERXND = RXSTOP; 00153 ETXST = TXSTART; 00154 00155 // Write a permanant per packet control byte of 0x00 00156 EWRPT = TXSTART; 00157 MACPut(0x00); 00158 00159 // Configure Receive Filters 00160 // (No need to reconfigure - Unicast OR Broadcast with CRC checking is 00161 // acceptable) 00162 //ERXFCON = ERXFCON_CRCEN; // Promiscious mode 00163 ERXFCON = 0xA3; // MODIFIX: Enable Multicasts 00164 00165 // Configure the MAC 00166 // Enable the receive portion of the MAC 00167 MACON1 = MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN; Nop(); 00168 00169 // Pad packets to 60 bytes, add CRC, and check Type/Length field. 00170 #if defined(FULL_DUPLEX) 00171 MACON3 = MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX; Nop(); 00172 MABBIPG = 0x15; Nop(); 00173 #else 00174 MACON3 = MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN; Nop(); 00175 MABBIPG = 0x12; Nop(); 00176 #endif 00177 00178 // Allow infinite deferals if the medium is continuously busy 00179 // (do not time out a transmission if the half duplex medium is 00180 // completely saturated with other people's data) 00181 MACON4 = MACON4_DEFER; Nop(); 00182 00183 // Set non-back-to-back inter-packet gap to 9.6us. The back-to-back 00184 // inter-packet gap (MABBIPG) is set by MACSetDuplex() which is called 00185 // later. 00186 MAIPGL = 0x12; Nop(); 00187 MAIPGH = 0x0C; Nop(); 00188 00189 // Set the maximum packet size which the controller will accept 00190 MAMXFLL = LOW(6+6+2+1500+4); Nop(); 00191 MAMXFLH = HIGH(6+6+2+1500+4); Nop(); 00192 00193 // MODIFIX: Initialize physical MAC address registers from flash. 00194 MAADR1 = pu8MAC[0]; Nop(); 00195 MAADR2 = pu8MAC[1]; Nop(); 00196 MAADR3 = pu8MAC[2]; Nop(); 00197 MAADR4 = pu8MAC[3]; Nop(); 00198 MAADR5 = pu8MAC[4]; Nop(); 00199 MAADR6 = pu8MAC[5]; Nop(); 00200 00201 // Disable half duplex loopback in PHY and set RXAPDIS bit as per errata 00202 WritePHYReg(PHCON2, PHCON2_HDLDIS | PHCON2_RXAPDIS); 00203 00204 // Configure LEDA to display LINK status, LEDB to display TX/RX activity 00205 SetLEDConfig(0x3472); 00206 00207 // Set the PHY into the proper duplex state 00208 #if defined(FULL_DUPLEX) 00209 WritePHYReg(PHCON1, PHCON1_PDPXMD); 00210 #else 00211 WritePHYReg(PHCON1, 0x0000); 00212 #endif 00213 00214 // Enable packet reception 00215 ECON1bits.RXEN = 1; 00216 }//end MACInit 00217 00218 00219 /****************************************************************************** 00220 * Function: BOOL MACIsLinked(void) 00221 * 00222 * PreCondition: None 00223 * 00224 * Input: None 00225 * 00226 * Output: TRUE: If the PHY reports that a link partner is present 00227 * and the link has been up continuously since the last 00228 * call to MACIsLinked() 00229 * FALSE: If the PHY reports no link partner, or the link went 00230 * down momentarily since the last call to MACIsLinked() 00231 * 00232 * Side Effects: None 00233 * 00234 * Overview: Returns the PHSTAT1.LLSTAT bit. 00235 * 00236 * Note: None 00237 *****************************************************************************/ 00238 /*BOOL MACIsLinked(void) 00239 { 00240 // LLSTAT is a latching low link status bit. Therefore, if the link 00241 // goes down and comes back up before a higher level stack program calls 00242 // bMACIsLinked(), bMACIsLinked() will still return FALSE. The next 00243 // call to MACIsLinked() will return TRUE (unless the link goes down 00244 // again). 00245 return ReadPHYReg(PHSTAT1).PHSTAT1bits.LLSTAT; 00246 } 00247 */ 00248 BOOL MACIsLinked( void ) 00249 { 00250 // MODIFIX: We simply check the link status LED, whether there is a link. 00252 return PORTAbits.RA0; 00253 } 00254 00255 /****************************************************************************** 00256 * Function: BOOL MACIsTxReady(void) 00257 * 00258 * PreCondition: None 00259 * 00260 * Input: None 00261 * 00262 * Output: TRUE: If no Ethernet transmission is in progress 00263 * FALSE: If a previous transmission was started, and it has 00264 * not completed yet. While FALSE, the data in the 00265 * transmit buffer and the TXST/TXND pointers must not 00266 * be changed. 00267 * 00268 * Side Effects: None 00269 * 00270 * Overview: Returns the ECON1.TXRTS bit 00271 * 00272 * Note: None 00273 *****************************************************************************/ 00274 BOOL MACIsTxReady(void) 00275 { 00276 if(!ECON1bits.TXRTS) 00277 return TRUE; 00278 00279 // Retry transmission if the current packet seems to be not completing 00280 // Wait 3ms before triggering the retry. 00281 if((WORD)TickGet() - wTXWatchdog >= (3ull*TICK_SECOND/1000ull)) 00282 { 00283 ECON1bits.TXRTS = 0; 00284 MACFlush(); 00285 } 00286 00287 return FALSE; 00288 } 00289 00290 /****************************************************************************** 00291 * Function: void MACDiscardRx(void) 00292 * 00293 * PreCondition: None 00294 * 00295 * Input: None 00296 * 00297 * Output: None 00298 * 00299 * Side Effects: None 00300 * 00301 * Overview: Marks the last received packet (obtained using 00302 * MACGetHeader())as being processed and frees the buffer 00303 * memory associated with it 00304 * 00305 * Note: Is is safe to call this function multiple times between 00306 * MACGetHeader() calls. Extra packets won't be thrown away 00307 * until MACGetHeader() makes it available. 00308 *****************************************************************************/ 00309 void MACDiscardRx(void) 00310 { 00311 WORD_VAL NewRXRDLocation; 00312 00313 // Make sure the current packet was not already discarded 00314 if(WasDiscarded) 00315 return; 00316 WasDiscarded = TRUE; 00317 00318 // Decrement the next packet pointer before writing it into 00319 // the ERXRDPT registers. This is a silicon errata workaround. 00320 // RX buffer wrapping must be taken into account if the 00321 // NextPacketLocation is precisely RXSTART. 00322 NewRXRDLocation.Val = NextPacketLocation.Val - 1; 00323 #if RXSTART == 0 00324 if(NewRXRDLocation.Val > RXSTOP) 00325 #else 00326 if(NewRXRDLocation.Val < RXSTART || NewRXRDLocation.Val > RXSTOP) 00327 #endif 00328 { 00329 NewRXRDLocation.Val = RXSTOP; 00330 } 00331 00332 // Decrement the RX packet counter register, EPKTCNT 00333 ECON2bits.PKTDEC = 1; 00334 00335 // Move the receive read pointer to unwrite-protect the memory used by the 00336 // last packet. The writing order is important: set the low byte first, 00337 // high byte last. 00338 ERXRDPTL = NewRXRDLocation.v[0]; 00339 ERXRDPTH = NewRXRDLocation.v[1]; 00340 00341 // The PKTIF flag should automatically be cleared by hardware, but 00342 // early beta silicon requires that you manually clear it. This should be 00343 // unneeded for production A0 silicon and later. 00344 EIRbits.PKTIF = 0; 00345 } 00346 00347 00348 /****************************************************************************** 00349 * Function: WORD MACGetFreeRxSize(void) 00350 * 00351 * PreCondition: None 00352 * 00353 * Input: None 00354 * 00355 * Output: A WORD estimate of how much RX buffer space is free at 00356 * the present time. 00357 * 00358 * Side Effects: None 00359 * 00360 * Overview: None 00361 * 00362 * Note: None 00363 *****************************************************************************/ 00364 WORD MACGetFreeRxSize(void) 00365 { 00366 WORD_VAL ReadPT, WritePT; 00367 00368 // Read the Ethernet hardware buffer write pointer. Because packets can be 00369 // received at any time, it can change between reading the low and high 00370 // bytes. A loop is necessary to make certain a proper low/high byte pair 00371 // is read. 00372 do { 00373 // Save EPKTCNT in a temporary location 00374 ReadPT.v[0] = EPKTCNT; 00375 00376 WritePT.Val = ERXWRPT; 00377 } while(EPKTCNT != ReadPT.v[0]); 00378 00379 // Determine where the write protection pointer is 00380 ReadPT.Val = ERXRDPT; 00381 00382 00383 // Calculate the difference between the pointers, taking care to account 00384 // for buffer wrapping conditions 00385 if(WritePT.Val > ReadPT.Val) 00386 { 00387 return (RXSTOP - RXSTART) - (WritePT.Val - ReadPT.Val); 00388 } 00389 else if(WritePT.Val == ReadPT.Val) 00390 { 00391 return RXSIZE - 1; 00392 } 00393 else 00394 { 00395 return ReadPT.Val - WritePT.Val - 1; 00396 } 00397 } 00398 00399 /****************************************************************************** 00400 * Function: BOOL MACGetHeader(MAC_ADDR *remote, BYTE* type) 00401 * 00402 * PreCondition: None 00403 * 00404 * Input: *remote: Location to store the Source MAC address of the 00405 * received frame. 00406 * *type: Location of a BYTE to store the constant 00407 * MAC_UNKNOWN, ETHER_IP, or ETHER_ARP, representing 00408 * the contents of the Ethernet type field. 00409 * 00410 * Output: TRUE: If a packet was waiting in the RX buffer. The 00411 * remote, and type values are updated. 00412 * FALSE: If a packet was not pending. remote and type are 00413 * not changed. 00414 * 00415 * Side Effects: Last packet is discarded if MACDiscardRx() hasn't already 00416 * been called. 00417 * 00418 * Overview: None 00419 * 00420 * Note: None 00421 *****************************************************************************/ 00422 BOOL MACGetHeader(MAC_ADDR *remote, BYTE* type) 00423 { 00424 ENC_PREAMBLE header; 00425 00426 // Test if at least one packet has been received and is waiting 00427 if(EPKTCNT == 0u) 00428 { 00429 return FALSE; 00430 } 00431 00432 // Make absolutely certain that any previous packet was discarded 00433 if(WasDiscarded == FALSE) 00434 { 00435 MACDiscardRx(); 00436 return FALSE; 00437 } 00438 // Save the location of this packet 00439 CurrentPacketLocation.Val = NextPacketLocation.Val; 00440 00441 // Set the read pointer to the beginning of the next unprocessed packet 00442 ERDPT = CurrentPacketLocation.Val; 00443 00444 // Obtain the MAC header from the Ethernet buffer 00445 MACGetArray((BYTE*)&header, sizeof(header)); 00446 00447 // The EtherType field, like most items transmitted on the Ethernet medium 00448 // are in big endian. 00449 header.Type.Val = swaps(header.Type.Val); 00450 00451 // Do a sanity check. There might be a bug in code someplace if this 00452 // Reset() ever happens. Check for potential errors in array/pointer writing code. 00453 if(header.NextPacketPointer > RXSTOP || ((BYTE_VAL*)(&header.NextPacketPointer))->bits.b0 || 00454 header.StatusVector.bits.Zero || 00455 header.StatusVector.bits.CRCError || 00456 header.StatusVector.bits.ByteCount > 1518u || 00457 !header.StatusVector.bits.ReceiveOk) 00458 { 00459 Reset(); 00460 } 00461 00462 // Save the location where the hardware will write the next packet to 00463 NextPacketLocation.Val = header.NextPacketPointer; 00464 00465 // Return the Ethernet frame's Source MAC address field to the caller 00466 // This parameter is useful for replying to requests without requiring an 00467 // ARP cycle. 00468 memcpy((void*)remote->v, (void*)header.SourceMACAddr.v, sizeof(*remote)); 00469 00470 // Return a simplified version of the EtherType field to the caller 00471 *type = MAC_UNKNOWN; 00472 if( (header.Type.v[1] == 0x08u) && 00473 ((header.Type.v[0] == ETHER_IP) || (header.Type.v[0] == ETHER_ARP)) ) 00474 { 00475 *type = header.Type.v[0]; 00476 } 00477 00478 // Mark this packet as discardable 00479 WasDiscarded = FALSE; 00480 return TRUE; 00481 } 00482 00483 00484 /****************************************************************************** 00485 * Function: void MACPutHeader(MAC_ADDR *remote, BYTE type, WORD dataLen) 00486 * 00487 * PreCondition: MACIsTxReady() must return TRUE. 00488 * 00489 * Input: *remote: Pointer to memory which contains the destination 00490 * MAC address (6 bytes) 00491 * type: The constant ETHER_ARP or ETHER_IP, defining which 00492 * value to write into the Ethernet header's type field. 00493 * dataLen: Length of the Ethernet data payload 00494 * 00495 * Output: None 00496 * 00497 * Side Effects: None 00498 * 00499 * Overview: None 00500 * 00501 * Note: Because of the dataLen parameter, it is probably 00502 * advantagous to call this function immediately before 00503 * transmitting a packet rather than initially when the 00504 * packet is first created. The order in which the packet 00505 * is constructed (header first or data first) is not 00506 * important. 00507 *****************************************************************************/ 00508 void MACPutHeader(MAC_ADDR *remote, BYTE type, WORD dataLen) 00509 { 00510 // Set the write pointer to the beginning of the transmit buffer 00511 EWRPT = TXSTART + 1; 00512 00513 // Calculate where to put the TXND pointer 00514 dataLen += (WORD)sizeof(ETHER_HEADER) + TXSTART; 00515 00516 // Write the TXND pointer into the registers, given the dataLen given 00517 ETXND = dataLen; 00518 00519 // Set the per-packet control byte and write the Ethernet destination 00520 // address 00521 MACPutArray((BYTE*)remote, sizeof(*remote)); 00522 00523 // MODIFIX: Write our MAC address from flash in the Ethernet source field 00524 MACPutROMArray( ( ROM BYTE * ) pu8MAC, sizeof( MAC_ADDR ) ); 00525 00526 // Write the appropriate Ethernet Type WORD for the protocol being used 00527 MACPut(0x08); 00528 MACPut((type == MAC_IP) ? ETHER_IP : ETHER_ARP); 00529 } 00530 00531 /****************************************************************************** 00532 * Function: void MACFlush(void) 00533 * 00534 * PreCondition: A packet has been created by calling MACPut() and 00535 * MACPutHeader(). 00536 * 00537 * Input: None 00538 * 00539 * Output: None 00540 * 00541 * Side Effects: None 00542 * 00543 * Overview: MACFlush causes the current TX packet to be sent out on 00544 * the Ethernet medium. The hardware MAC will take control 00545 * and handle CRC generation, collision retransmission and 00546 * other details. 00547 * 00548 * Note: After transmission completes (MACIsTxReady() returns TRUE), 00549 * the packet can be modified and transmitted again by calling 00550 * MACFlush() again. Until MACPutHeader() or MACPut() is 00551 * called (in the TX data area), the data in the TX buffer 00552 * will not be corrupted. 00553 *****************************************************************************/ 00554 void MACFlush(void) 00555 { 00556 // Reset the Ethernet TX logic. This is a (suspected) errata workaround to 00557 // prevent the TXRTS bit from getting stuck set indefinitely, causing the 00558 // stack to lock up under certain bad conditions. 00559 ECON1bits.TXRST = 1; 00560 ECON1bits.TXRST = 0; 00561 00562 // Wait at least 1.6us after TX Reset before setting TXRTS. 00563 // If you don't wait long enough, the TX logic won't be finished resetting. 00564 {volatile BYTE i = 8; while(i--);} 00565 EIRbits.TXERIF = 0; 00566 00567 // Start the transmission 00568 // After transmission completes (MACIsTxReady() returns TRUE), the packet 00569 // can be modified and transmitted again by calling MACFlush() again. 00570 // Until MACPutHeader() is called, the data in the TX buffer will not be 00571 // corrupted. 00572 ECON1bits.TXRTS = 1; 00573 wTXWatchdog = TickGet(); 00574 } 00575 00576 00577 /****************************************************************************** 00578 * Function: void MACSetReadPtrInRx(WORD offset) 00579 * 00580 * PreCondition: A packet has been obtained by calling MACGetHeader() and 00581 * getting a TRUE result. 00582 * 00583 * Input: offset: WORD specifying how many bytes beyond the Ethernet 00584 * header's type field to relocate the SPI read 00585 * pointer. 00586 * 00587 * Output: None 00588 * 00589 * Side Effects: None 00590 * 00591 * Overview: SPI read pointer are updated. All calls to 00592 * MACGet() and MACGetArray() will use these new values. 00593 * 00594 * Note: RXSTOP must be statically defined as being > RXSTART for 00595 * this function to work correctly. In other words, do not 00596 * define an RX buffer which spans the 0x1FFF->0x0000 memory 00597 * boundary. 00598 *****************************************************************************/ 00599 void MACSetReadPtrInRx(WORD offset) 00600 { 00601 WORD_VAL ReadPT; 00602 00603 // Determine the address of the beginning of the entire packet 00604 // and adjust the address to the desired location 00605 ReadPT.Val = CurrentPacketLocation.Val + sizeof(ENC_PREAMBLE) + offset; 00606 00607 // Since the receive buffer is circular, adjust if a wraparound is needed 00608 if(ReadPT.Val > RXSTOP) 00609 ReadPT.Val -= RXSIZE; 00610 00611 // Set the read pointer to the new calculated value 00612 ERDPTL = ReadPT.v[0]; 00613 ERDPTH = ReadPT.v[1]; 00614 } 00615 00616 WORD_VAL u16MACGetCurrentReadPtrPos(void) 00617 { 00618 WORD_VAL ReadPT; 00619 ReadPT.v[0] = ERDPTL; 00620 ReadPT.v[1] = ERDPTH; 00621 return ReadPT; 00622 } 00623 00624 void MACSetCurrentReadPtrPos(WORD_VAL u16Pos) 00625 { 00626 ERDPTL = u16Pos.v[0]; 00627 ERDPTH = u16Pos.v[1]; 00628 } 00629 00630 /****************************************************************************** 00631 * Function: WORD MACSetWritePtr(WORD Address) 00632 * 00633 * PreCondition: None 00634 * 00635 * Input: Address: Address to seek to 00636 * 00637 * Output: WORD: Old EWRPT location 00638 * 00639 * Side Effects: None 00640 * 00641 * Overview: SPI write pointer is updated. All calls to 00642 * MACPut() and MACPutArray() will use this new value. 00643 * 00644 * Note: None 00645 *****************************************************************************/ 00646 WORD MACSetWritePtr(WORD address) 00647 { 00648 WORD oldVal; 00649 00650 oldVal = EWRPT; 00651 EWRPT = address; 00652 return oldVal; 00653 } 00654 00655 /****************************************************************************** 00656 * Function: WORD MACSetReadPtr(WORD Address) 00657 * 00658 * PreCondition: None 00659 * 00660 * Input: Address: Address to seek to 00661 * 00662 * Output: WORD: Old ERDPT value 00663 * 00664 * Side Effects: None 00665 * 00666 * Overview: SPI write pointer is updated. All calls to 00667 * MACPut() and MACPutArray() will use this new value. 00668 * 00669 * Note: None 00670 *****************************************************************************/ 00671 WORD MACSetReadPtr(WORD address) 00672 { 00673 WORD oldVal; 00674 00675 oldVal = ERDPT; 00676 ERDPT = address; 00677 return oldVal; 00678 } 00679 00680 00681 /****************************************************************************** 00682 * Function: WORD MACCalcRxChecksum(WORD offset, WORD len) 00683 * 00684 * PreCondition: None 00685 * 00686 * Input: offset - Number of bytes beyond the beginning of the 00687 * Ethernet data (first byte after the type field) 00688 * where the checksum should begin 00689 * len - Total number of bytes to include in the checksum 00690 * 00691 * Output: 16-bit checksum as defined by RFC 793. 00692 * 00693 * Side Effects: None 00694 * 00695 * Overview: This function performs a checksum calculation in the MAC 00696 * buffer itself 00697 * 00698 * Note: None 00699 *****************************************************************************/ 00700 WORD MACCalcRxChecksum(WORD offset, WORD len) 00701 { 00702 WORD temp; 00703 WORD RDSave; 00704 00705 // Add the offset requested by firmware plus the Ethernet header 00706 temp = CurrentPacketLocation.Val + sizeof(ENC_PREAMBLE) + offset; 00707 if(temp > RXSTOP) // Adjust value if a wrap is needed 00708 { 00709 temp -= RXSIZE; 00710 } 00711 00712 RDSave = ERDPT; 00713 ERDPT = temp; 00714 temp = CalcIPBufferChecksum(len); 00715 ERDPT = RDSave; 00716 00717 return temp; 00718 } 00719 00720 00721 /****************************************************************************** 00722 * Function: WORD CalcIPBufferChecksum(WORD len) 00723 * 00724 * PreCondition: Read buffer pointer set to starting of checksum data 00725 * 00726 * Input: len: Total number of bytes to calculate the checksum over. 00727 * The first byte included in the checksum is the byte 00728 * pointed to by ERDPT, which is updated by calls to 00729 * MACGet(), MACSetRxBuffer(), MACSetTxBuffer(), etc. 00730 * 00731 * Output: 16-bit checksum as defined by RFC 793 00732 * 00733 * Side Effects: None 00734 * 00735 * Overview: This function performs a checksum calculation in the MAC 00736 * buffer itself. The MAC has a hardware DMA module 00737 * which can calculate the checksum faster than software, so 00738 * this function replaces the CaclIPBufferChecksum() function 00739 * defined in the helpers.c file. Through the use of 00740 * preprocessor defines, this replacement is automatic. 00741 * 00742 * Note: This function works either in the RX buffer area or the TX 00743 * buffer area. No validation is done on the len parameter. 00744 *****************************************************************************/ 00745 /* 00746 WORD CalcIPBufferChecksum(WORD len) 00747 { 00748 WORD_VAL temp; 00749 00750 // Take care of special cases which the DMA cannot be used for 00751 if(len == 0u) 00752 { 00753 return 0xFFFF; 00754 } 00755 else if(len == 1u) 00756 { 00757 return ~((WORD)MACGet()); 00758 } 00759 00760 00761 // Set the DMA starting address to the RAM read pointer value 00762 temp.Val = ERDPT; 00763 EDMAST = temp.Val; 00764 00765 // See if we are calculating a checksum within the RX buffer (where 00766 // wrapping rules apply) or TX/unused area (where wrapping rules are 00767 // not applied) 00768 #if RXSTART == 0 00769 if(temp.Val <= RXSTOP) 00770 #else 00771 if(temp.Val >= RXSTART && temp.Val <= RXSTOP) 00772 #endif 00773 { 00774 // Calculate the DMA ending address given the starting address and len 00775 // parameter. The DMA will follow the receive buffer wrapping boundary. 00776 temp.Val += len-1; 00777 if(temp.Val > RXSTOP) 00778 { 00779 temp.Val -= RXSIZE; 00780 } 00781 } 00782 else 00783 { 00784 temp.Val += len-1; 00785 } 00786 00787 // Write the DMA end address 00788 EDMAND = temp.Val; 00789 00790 // Begin the DMA checksum calculation and wait until it is finished 00791 ECON1bits.CSUMEN = 1; 00792 ECON1bits.DMAST = 1; 00793 while(ECON1bits.DMAST); 00794 00795 // Return the resulting good stuff 00796 return (((WORD)EDMACSL)<<8) | EDMACSH; 00797 } 00798 */ 00799 00800 /****************************************************************************** 00801 * Function: WORD CalcIPBufferChecksum(WORD len) 00802 * 00803 * PreCondition: Read buffer pointer set to starting of checksum data 00804 * 00805 * Input: len: Total number of bytes to calculate the checksum over. 00806 * The first byte included in the checksum is the byte 00807 * pointed to by ERDPT, which is updated by calls to 00808 * MACSetReadPtr(), MACGet(), MACGetArray(), 00809 * MACGetHeader(), etc. 00810 * 00811 * Output: 16-bit checksum as defined by RFC 793 00812 * 00813 * Side Effects: None 00814 * 00815 * Overview: This function performs a checksum calculation in the MAC 00816 * buffer itself 00817 * 00818 * Note: This function works either in the RX buffer area or the TX 00819 * buffer area. No validation is done on the len parameter. 00820 *****************************************************************************/ 00821 WORD CalcIPBufferChecksum(WORD len) 00822 { 00823 WORD Start; 00824 DWORD_VAL Checksum = {0x00000000ul}; 00825 WORD ChunkLen; 00826 BYTE DataBuffer[20]; // Must be an even size 00827 WORD *DataPtr; 00828 00829 // Save the read pointer starting address 00830 Start = ERDPT; 00831 00832 while(len) 00833 { 00834 // Obtain a chunk of data (less SPI overhead compared 00835 // to requesting one byte at a time) 00836 ChunkLen = len > sizeof(DataBuffer) ? sizeof(DataBuffer) : len; 00837 MACGetArray(DataBuffer, ChunkLen); 00838 00839 len -= ChunkLen; 00840 00841 // Take care of a last odd numbered data byte 00842 if(((WORD_VAL*)&ChunkLen)->bits.b0) 00843 { 00844 DataBuffer[ChunkLen] = 0x00; 00845 ChunkLen++; 00846 } 00847 00848 // Calculate the checksum over this chunk 00849 DataPtr = (WORD*)&DataBuffer[0]; 00850 while(ChunkLen) 00851 { 00852 Checksum.Val += *DataPtr++; 00853 ChunkLen -= 2; 00854 } 00855 } 00856 00857 // Restore old read pointer location 00858 ERDPT = Start; 00859 00860 // Do an end-around carry (one's complement arrithmatic) 00861 Checksum.Val = (DWORD)Checksum.w[0] + (DWORD)Checksum.w[1]; 00862 00863 // Do another end-around carry in case if the prior add 00864 // caused a carry out 00865 Checksum.w[0] += Checksum.w[1]; 00866 00867 // Return the resulting checksum 00868 return ~Checksum.w[0]; 00869 } 00870 00871 /****************************************************************************** 00872 * Function: void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len) 00873 * 00874 * PreCondition: None 00875 * 00876 * Input: destAddr: Destination address in the Ethernet memory to 00877 * copy to. If the MSb is set, the current EWRPT 00878 * value will be used instead. 00879 * sourceAddr: Source address to read from. If the MSb is 00880 * set, the current ERDPT value will be used 00881 * instead. 00882 * len: Number of bytes to copy 00883 * 00884 * Output: None 00885 * 00886 * Side Effects: None 00887 * 00888 * Overview: Bytes are asynchrnously transfered within the buffer. Call 00889 * MACIsMemCopyDone() to see when the transfer is complete. 00890 * 00891 * Note: If a prior transfer is already in progress prior to 00892 * calling this function, this function will block until it 00893 * can start this transfer. 00894 *****************************************************************************/ 00895 void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len) 00896 { 00897 WORD_VAL ReadSave, WriteSave; 00898 BOOL UpdateWritePointer = FALSE; 00899 BOOL UpdateReadPointer = FALSE; 00900 00901 if(((WORD_VAL*)&destAddr)->bits.b15) 00902 { 00903 UpdateWritePointer = TRUE; 00904 destAddr = EWRPT; 00905 } 00906 if(((WORD_VAL*)&sourceAddr)->bits.b15) 00907 { 00908 UpdateReadPointer = TRUE; 00909 sourceAddr = ERDPT; 00910 } 00911 00912 // Handle special conditions where len == 0 or len == 1 00913 // The DMA module is not capable of handling those corner cases 00914 if(len <= 1u) 00915 { 00916 ReadSave.Val = ERDPT; 00917 WriteSave.Val = EWRPT; 00918 ERDPT = sourceAddr; 00919 EWRPT = destAddr; 00920 while(len--) 00921 MACPut(MACGet()); 00922 if(!UpdateReadPointer) 00923 { 00924 ERDPT = ReadSave.Val; 00925 } 00926 if(!UpdateWritePointer) 00927 { 00928 EWRPT = WriteSave.Val; 00929 } 00930 } 00931 else 00932 { 00933 if(UpdateWritePointer) 00934 { 00935 WriteSave.Val = destAddr + len; 00936 EWRPT = WriteSave.Val; 00937 } 00938 len += sourceAddr - 1; 00939 while(ECON1bits.DMAST); 00940 EDMAST = sourceAddr; 00941 EDMADST = destAddr; 00942 if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART)) 00943 len -= RXSIZE; 00944 EDMAND = len; 00945 ECON1bits.CSUMEN = 0; 00946 ECON1bits.DMAST = 1; 00947 while(ECON1bits.DMAST); // DMA errata workaround: Must not access EDATA while DMA active 00948 00949 if(UpdateReadPointer) 00950 { 00951 len++; 00952 if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART)) 00953 len -= RXSIZE; 00954 ERDPT = len; 00955 } 00956 } 00957 } 00958 00959 /* 00960 void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len) 00961 { 00962 WORD_VAL ReadSave, WriteSave; 00963 BOOL UpdateWritePointer = FALSE; 00964 BOOL UpdateReadPointer = FALSE; 00965 00966 if(((WORD_VAL*)&destAddr)->bits.b15) 00967 { 00968 UpdateWritePointer = TRUE; 00969 destAddr = EWRPT; 00970 } 00971 if(((WORD_VAL*)&sourceAddr)->bits.b15) 00972 { 00973 UpdateReadPointer = TRUE; 00974 sourceAddr = ERDPT; 00975 } 00976 00977 ReadSave.Val = ERDPT; 00978 WriteSave.Val = EWRPT; 00979 ERDPT = sourceAddr; 00980 EWRPT = destAddr; 00981 while(len--) 00982 { 00983 MACPut(MACGet()); 00984 } 00985 00986 if(!UpdateReadPointer) 00987 { 00988 ERDPT = ReadSave.Val; 00989 } 00990 if(!UpdateWritePointer) 00991 { 00992 EWRPT = WriteSave.Val; 00993 } 00994 } 00995 */ 00996 00997 BOOL MACIsMemCopyDone(void) 00998 { 00999 return !ECON1bits.DMAST; 01000 } 01001 01002 /****************************************************************************** 01003 * Function: BYTE MACGet() 01004 * 01005 * PreCondition: ERDPT must point to the place to read from. 01006 * 01007 * Input: None 01008 * 01009 * Output: Byte read from the Ethernet's buffer RAM 01010 * 01011 * Side Effects: None 01012 * 01013 * Overview: MACGet returns the byte pointed to by ERDPT and 01014 * increments ERDPT so MACGet() can be called again. The 01015 * increment will follow the receive buffer wrapping boundary. 01016 * 01017 * Note: For better performance, implement this function as a macro: 01018 * #define MACGet() (EDATA) 01019 *****************************************************************************/ 01020 BYTE MACGet() 01021 { 01022 return EDATA; 01023 }//end MACGet 01024 01025 01026 /****************************************************************************** 01027 * Function: WORD MACGetArray(BYTE *val, WORD len) 01028 * 01029 * PreCondition: ERDPT must point to the place to read from. 01030 * 01031 * Input: *val: Pointer to storage location 01032 * len: Number of bytes to read from the data buffer. 01033 * 01034 * Output: Byte(s) of data read from the data buffer. 01035 * 01036 * Side Effects: None 01037 * 01038 * Overview: Reads several sequential bytes from the data buffer 01039 * and places them into local memory. ERDPT is incremented 01040 * after each byte, following the same rules as MACGet(). 01041 * 01042 * Note: None 01043 *****************************************************************************/ 01044 WORD MACGetArray(BYTE *val, WORD len) 01045 { 01046 WORD w; 01047 volatile BYTE i; 01048 01049 w = len; 01050 if(val) 01051 { 01052 while(w--) 01053 { 01054 *val++ = EDATA; 01055 } 01056 } 01057 else 01058 { 01059 while(w--) 01060 { 01061 i = EDATA; 01062 } 01063 } 01064 01065 return len; 01066 }//end MACGetArray 01067 01068 01069 /****************************************************************************** 01070 * Function: void MACPut(BYTE val) 01071 * 01072 * PreCondition: EWRPT must point to the location to begin writing. 01073 * 01074 * Input: Byte to write into the Ethernet buffer memory 01075 * 01076 * Output: None 01077 * 01078 * Side Effects: None 01079 * 01080 * Overview: Writes to the EDATA register, which will indirectly 01081 * increment EWRPTH:EWRPTL. 01082 * 01083 * Note: None 01084 *****************************************************************************/ 01085 void MACPut(BYTE val) 01086 { 01087 // Note: Due to a PIC18F97J60 bug, you must use the MOVFF instruction to 01088 // write to EDATA or else the read pointer (ERDPT) will inadvertently 01089 // increment. 01090 PRODL = val; 01091 #if defined(HI_TECH_C) 01092 asm("movff _PRODL, _EDATA"); 01093 #else 01094 _asm movff PRODL, EDATA _endasm 01095 #endif 01096 }//end MACPut 01097 01098 01099 /****************************************************************************** 01100 * Function: void MACPutArray(BYTE *val, WORD len) 01101 * 01102 * PreCondition: EWRPT must point to the location to begin writing. 01103 * 01104 * Input: *val: Pointer to source of bytes to copy. 01105 * len: Number of bytes to write to the data buffer. 01106 * 01107 * Output: None 01108 * 01109 * Side Effects: None 01110 * 01111 * Overview: MACPutArray writes several sequential bytes to the 01112 * Ethernet buffer RAM. It performs faster than multiple MACPut() 01113 * calls. EWRPT is incremented by len. 01114 * 01115 * Note: None 01116 *****************************************************************************/ 01117 void MACPutArray(BYTE *val, WORD len) 01118 { 01119 while(len--) 01120 { 01121 // Note: Due to a PIC18F97J60 bug, you must use the MOVFF instruction to 01122 // write to EDATA or else the read pointer (ERDPT) will inadvertently 01123 // increment. 01124 PRODL = *val++; 01125 #if defined(HI_TECH_C) 01126 asm("movff _PRODL, _EDATA"); 01127 #else 01128 _asm movff PRODL, EDATA _endasm 01129 #endif 01130 } 01131 }//end MACPutArray 01132 01133 void MACPutROMArray(ROM BYTE *val, WORD len) 01134 { 01135 while(len--) 01136 { 01137 // Note: Due to a PIC18F97J60 bug, you must use the MOVFF instruction to 01138 // write to EDATA or else the read pointer (ERDPT) will inadvertently 01139 // increment. 01140 PRODL = *val++; 01141 #if defined(HI_TECH_C) 01142 asm("movff _PRODL, _EDATA"); 01143 #else 01144 _asm movff PRODL, EDATA _endasm 01145 #endif 01146 } 01147 }//end MACPutROMArray 01148 01149 01150 /****************************************************************************** 01151 * Function: ReadPHYReg 01152 * 01153 * PreCondition: Ethernet module must be enabled (ECON1.ETHEN = 1). 01154 * 01155 * Input: Address of the PHY register to read from. 01156 * 01157 * Output: 16 bits of data read from the PHY register. 01158 * 01159 * Side Effects: None 01160 * 01161 * Overview: ReadPHYReg performs an MII read operation. While in 01162 * progress, it simply polls the MII BUSY bit wasting time 01163 * (10.24us). 01164 * 01165 * Note: None 01166 *****************************************************************************/ 01167 PHYREG ReadPHYReg(BYTE Register) 01168 { 01169 PHYREG Result; 01170 01171 // Set the right address and start the register read operation 01172 MIREGADR = Register; Nop(); 01173 MICMD = MICMD_MIIRD; Nop(); 01174 01175 // Loop to wait until the PHY register has been read through the MII 01176 // This requires 10.24us 01177 while(MISTATbits.BUSY); 01178 01179 // Stop reading 01180 MICMD = 0x00; Nop(); 01181 01182 // Obtain results and return 01183 Result.VAL.v[0] = MIRDL; 01184 Nop(); 01185 Result.VAL.v[1] = MIRDH; 01186 01187 return Result; 01188 }//end ReadPHYReg 01189 01190 01191 /****************************************************************************** 01192 * Function: WritePHYReg 01193 * 01194 * PreCondition: Ethernet module must be enabled (ECON1.ETHEN = 1). 01195 * 01196 * Input: Address of the PHY register to write to. 01197 * 16 bits of data to write to PHY register. 01198 * 01199 * Output: None 01200 * 01201 * Side Effects: None 01202 * 01203 * Overview: WritePHYReg performs an MII write operation. While in 01204 * progress, it simply polls the MII BUSY bit wasting time 01205 * (10.24us). 01206 * 01207 * Note: None 01208 *****************************************************************************/ 01209 void WritePHYReg(BYTE Register, WORD Data) 01210 { 01211 BYTE GIESave; 01212 01213 // Write the register address 01214 MIREGADR = Register; 01215 01216 // Write the data through the MIIM interface 01217 // Order is important: write low byte first, high byte last 01218 // 01219 // Due to a silicon problem, you cannot access any register with LSb address 01220 // bits of 0x16 between your write to MIWRL and MIWRH or else the value in 01221 // MIWRL will be corrupted. This inline assembly prevents this by copying 01222 // the value to PRODH:PRODL first, which is at fixed locations of 01223 // 0xFF4:0xFF3. These addresses have LSb address bits of 0x14 and 0x13. 01224 // Interrupts must be disabled to prevent arbitrary ISR code from accessing 01225 // memory with LSb bits of 0x16 and corrupting the MIWRL value. 01226 PRODL = ((WORD_VAL*)&Data)->v[0]; 01227 PRODH = ((WORD_VAL*)&Data)->v[1]; 01228 GIESave = INTCON & 0xC0; // Save GIEH and GIEL bits 01229 INTCON &= 0x3F; // Clear INTCONbits.GIEH and INTCONbits.GIEL 01230 #if defined(HI_TECH_C) 01231 asm("movff _PRODL, _MIWRL"); 01232 asm("nop"); 01233 asm("movff _PRODH, _MIWRH"); 01234 #else 01235 _asm 01236 movff PRODL, MIWRL 01237 nop 01238 movff PRODH, MIWRH 01239 _endasm 01240 #endif 01241 INTCON |= GIESave; // Restore GIEH and GIEL value 01242 01243 // Wait until the PHY register has been written 01244 // This operation requires 10.24us 01245 while(MISTATbits.BUSY); 01246 }//end WritePHYReg 01247 01248 01249 /****************************************************************************** 01250 * Function: void MACPowerDown(void) 01251 * 01252 * PreCondition: None 01253 * 01254 * Input: None 01255 * 01256 * Output: None 01257 * 01258 * Side Effects: None 01259 * 01260 * Overview: MACPowerDown disables the Ethernet module. 01261 * All MAC and PHY registers should not be accessed. 01262 * 01263 * Note: Normally, this function would be called before putting the 01264 * PIC to sleep. If a packet is being transmitted while this 01265 * function is called, this function will block until it is 01266 * it complete. If anything is being received, it will be 01267 * completed. 01268 * 01269 * The Ethernet module will continue to draw significant 01270 * power in sleep mode if this function is not called first. 01271 *****************************************************************************/ 01272 void MACPowerDown(void) 01273 { 01274 // Disable packet reception 01275 ECON1bits.RXEN = 0; 01276 01277 // Make sure any last packet which was in-progress when RXEN was cleared 01278 // is completed 01279 while(ESTATbits.RXBUSY); 01280 01281 // If a packet is being transmitted, wait for it to finish 01282 while(ECON1bits.TXRTS); 01283 01284 // Disable the Ethernet module 01285 ECON2bits.ETHEN = 0; 01286 01287 // Switch off LEDs. 01288 LATAbits.LATA0 = 0; 01289 LATAbits.LATA1 = 0; 01290 01291 }//end MACPowerDown 01292 01293 /****************************************************************************** 01294 * Function: void MACPowerUp(void) 01295 * 01296 * PreCondition: None 01297 * 01298 * Input: None 01299 * 01300 * Output: None 01301 * 01302 * Side Effects: None 01303 * 01304 * Overview: MACPowerUp returns the Ethernet module back to normal operation 01305 * after a previous call to MACPowerDown(). Calling this 01306 * function when already powered up will have no effect. 01307 * 01308 * Note: If a link partner is present, it will take 10s of 01309 * milliseconds before a new link will be established after 01310 * waking up. While not linked, packets which are 01311 * transmitted will most likely be lost. MACIsLinked() can 01312 * be called to determine if a link is established. 01313 *****************************************************************************/ 01314 void MACPowerUp(void) 01315 { 01316 // Power up the Ethernet module 01317 ECON2bits.ETHEN = 1; 01318 01319 // Wait for PHY to become ready 01320 while(!ESTATbits.PHYRDY) 01321 01322 // Enable packet reception 01323 ECON1bits.RXEN = 1; 01324 }//end MACPowerUp 01325 01326 01327 01328 /****************************************************************************** 01329 * Function: void SetRXHashTableEntry(MAC_ADDR DestMACAddr) 01330 * 01331 * PreCondition: SPI bus must be initialized (done in MACInit()). 01332 * 01333 * Input: DestMACAddr: 6 byte group destination MAC address to allow 01334 * through the Hash Table Filter 01335 * 01336 * Output: Sets the appropriate bit in the EHT* registers to allow 01337 * packets sent to DestMACAddr to be received if the Hash 01338 * Table receive filter is enabled 01339 * 01340 * Side Effects: None 01341 * 01342 * Overview: Calculates a CRC-32 using polynomial 0x4C11DB7 and then, 01343 * using bits 28:23 of the CRC, sets the appropriate bit in 01344 * the EHT* registers 01345 * 01346 * Note: This code is commented out to save code space on systems 01347 * that do not need this function. Change the "#if 0" line 01348 * to "#if 1" to uncomment it. 01349 *****************************************************************************/ 01350 #if 0 01351 void SetRXHashTableEntry(MAC_ADDR DestMACAddr) 01352 { 01353 DWORD_VAL CRC = {0xFFFFFFFF}; 01354 BYTE *HTRegister; 01355 BYTE i, j; 01356 01357 // Calculate a CRC-32 over the 6 byte MAC address 01358 // using polynomial 0x4C11DB7 01359 for(i = 0; i < sizeof(MAC_ADDR); i++) 01360 { 01361 BYTE crcnext; 01362 01363 // shift in 8 bits 01364 for(j = 0; j < 8; j++) 01365 { 01366 crcnext = 0; 01367 if(((BYTE_VAL*)&(CRC.v[3]))->bits.b7) 01368 crcnext = 1; 01369 crcnext ^= (((BYTE_VAL*)&DestMACAddr.v[i])->bits.b0); 01370 01371 CRC.Val <<= 1; 01372 if(crcnext) 01373 CRC.Val ^= 0x4C11DB7; 01374 // next bit 01375 DestMACAddr.v[i] >>= 1; 01376 } 01377 } 01378 01379 // CRC-32 calculated, now extract bits 28:23 01380 // Bits 25:23 define where within the Hash Table byte the bit needs to be set 01381 // Bits 28:26 define which of the 8 Hash Table bytes that bits 25:23 apply to 01382 i = CRC.v[3] & 0x1F; 01383 HTRegister = (i >> 2) + &EHT0; 01384 i = (i << 1) & 0x06; 01385 ((BYTE_VAL*)&i)->bits.b0 = ((BYTE_VAL*)&CRC.v[2])->bits.b7; 01386 01387 // Set the proper bit in the Hash Table 01388 *HTRegister |= 1<<i; 01389 } 01390 #endif
1.5.5