////////////////////////////////////////////////////////////////////// // REALTEK RTL8019AS DRIVER // PACKET WHACKER // Author: Fred Eady // Version: 1.0 // Date: 10/13/01 // Description: ARP, PING, ECHO and Control, TCP ////////////////////////////////////////////////////////////////////// #include <16f877.h> #device *=16 #include #include #fuses HS,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,WRT,NODEBUG #use delay(clock=20000000) //Easy Ethernet //#use delay(clock=19662080) //PICDEM.net #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) #use fast_io(A) #use fast_io(B) #use fast_io(C) #use fast_io(D) #use fast_io(E) #define esc 0x1B //****************************************************************** //* FUNCTION PROTOTYPES //****************************************************************** #separate void show_aux_packet(); #separate void dump_header(); #separate void readwrite(); #separate void bin2hex(binchar); #separate void show_regs(); #separate void show_packet(); #separate void cls(); #separate void application_code(); #separate void tcp(); void assemble_ack(); void write_creg(int regaddr, int regdata); int8 read_creg(int regaddr); void get_packet(); void setipaddrs(); void cksum(); void echo_packet(); void send_tcp_packet(); void arp(); void icmp(); void udp(); //****************************************************************** //* IP ADDRESS DEFINITION //* This is the Ethernet Module IP address. //* You may change this to any valid address. //****************************************************************** int8 MYIP[4] = { 192,168,1,150 }; //****************************************************************** //* HARDWARE (MAC) ADDRESS DEFINITION //* This is the Ethernet Module hardware address. //* You may change this to any valid address. //* Realtek = 00-E0-4C-xx-xx-xx //****************************************************************** char MYMAC[6] = { 0x00,0xE0,0x4C,'M','H','1' }; //****************************************************************** //* Receive Ring Buffer Header Layout //* This is the 4-byte header that resides infront of the //* data packet in the receive buffer. //****************************************************************** unsigned int8 pageheader[4]; #define enetpacketstatus 0x00 #define nextblock_ptr 0x01 #define enetpacketLenL 0x02 #define enetpacketLenH 0x03 //****************************************************************** //* Ethernet Header Layout //****************************************************************** unsigned int8 packet[96]; //50 bytes of UDP data available #define enetpacketDest0 0x00 //destination mac address #define enetpacketDest1 0x01 #define enetpacketDest2 0x02 #define enetpacketDest3 0x03 #define enetpacketDest4 0x04 #define enetpacketDest5 0x05 #define enetpacketSrc0 0x06 //source mac address #define enetpacketSrc1 0x07 #define enetpacketSrc2 0x08 #define enetpacketSrc3 0x09 #define enetpacketSrc4 0x0A #define enetpacketSrc5 0x0B #define enetpacketType0 0x0C //type/length field #define enetpacketType1 0x0D #define enetpacketData 0x0E //IP data area begins here //****************************************************************** //* ARP Layout //****************************************************************** #define arp_hwtype 0x0E #define arp_prtype 0x10 #define arp_hwlen 0x12 #define arp_prlen 0x13 #define arp_op 0x14 #define arp_shaddr 0x16 //arp source mac address #define arp_sipaddr 0x1C //arp source ip address #define arp_thaddr 0x20 //arp target mac address #define arp_tipaddr 0x26 //arp target ip address //****************************************************************** //* IP Header Layout //****************************************************************** #define ip_vers_len 0x0E //IP version and header length #define ip_tos 0x0F //IP type of service #define ip_pktlen 0x10 //packet length #define ip_id 0x12 //datagram id #define ip_frag_offset 0x14 //fragment offset #define ip_ttl 0x16 //time to live #define ip_proto 0x17 //protocol (ICMP=1, TCP=6, UDP=11) #define ip_hdr_cksum 0x18 //header checksum #define ip_srcaddr 0x1A //IP address of source #define ip_destaddr 0x1E //IP addess of destination #define ip_data 0x22 //IP data area //****************************************************************** //* TCP Header Layout //****************************************************************** #define TCP_srcport 0x22 //TCP source port #define TCP_destport 0x24 //TCP destination port #define TCP_seqnum 0x26 //sequence number #define TCP_acknum 0x2A //acknowledgement number #define TCP_hdrflags 0x2E //4-bit header len and flags #define TCP_window 0x30 //window size #define TCP_cksum 0x32 //TCP checksum #define TCP_urgentptr 0x34 //urgent pointer #define TCP_data 0x36 //option/data //****************************************************************** //* TCP Flags //* IN flags represent incoming bits //* OUT flags represent outgoing bits //****************************************************************** #define FIN_IN bit_test(packet[TCP_hdrflags+1],0) #define SYN_IN bit_test(packet[TCP_hdrflags+1],1) #define RST_IN bit_test(packet[TCP_hdrflags+1],2) #define PSH_IN bit_test(packet[TCP_hdrflags+1],3) #define ACK_IN bit_test(packet[TCP_hdrflags+1],4) #define URG_IN bit_test(packet[TCP_hdrflags+1],5) #define FIN_OUT bit_set(packet[TCP_hdrflags+1],0); #define SYN_OUT bit_set(packet[TCP_hdrflags+1],1); #define RST_OUT bit_set(packet[TCP_hdrflags+1],2); #define PSH_OUT bit_set(packet[TCP_hdrflags+1],3); #define ACK_OUT bit_set(packet[TCP_hdrflags+1],4); #define URG_OUT bit_set(packet[TCP_hdrflags+1],5); //****************************************************************** //* Port Definitions //* This address is used by TCP and the Telnet function. //* This can be changed to any valid port number as long as //* you modify your code to recognize the new port number. //****************************************************************** #define MY_PORT_ADDRESS 0x1F98 // 8088 DECIMAL //****************************************************************** //* IP Protocol Types //****************************************************************** #define PROT_ICMP 0x01 #define PROT_TCP 0x06 #define PROT_UDP 0x11 //****************************************************************** //* ICMP Header //****************************************************************** #define ICMP_type ip_data #define ICMP_code ICMP_type+1 #define ICMP_cksum ICMP_code+1 #define ICMP_id ICMP_cksum+2 #define ICMP_seqnum ICMP_id+2 #define ICMP_data ICMP_seqnum+2 //****************************************************************** //* UDP Header //;****************************************************************** #define UDP_srcport ip_data #define UDP_destport UDP_srcport+2 #define UDP_len UDP_destport+2 #define UDP_cksum UDP_len+2 #define UDP_data UDP_cksum+2 //****************************************************************** //* REALTEK CONTROL REGISTER OFFSETS //* All offsets in Page 0 unless otherwise specified //****************************************************************** #define CR 0x00 #define PSTART 0x01 #define PAR0 0x01 // Page 1 #define CR9346 0x01 // Page 3 #define PSTOP 0x02 #define BNRY 0x03 #define TSR 0x04 #define TPSR 0x04 #define TBCR0 0x05 #define NCR 0x05 #define TBCR1 0x06 #define ISR 0x07 #define CURR 0x07 // Page 1 #define RSAR0 0x08 #define CRDA0 0x08 #define RSAR1 0x09 #define CRDAL 0x09 #define RBCR0 0x0A #define RBCR1 0x0B #define RSR 0x0C #define RCR 0x0C #define TCR 0x0D #define CNTR0 0x0D #define DCR 0x0E #define CNTR1 0x0E #define IMR 0x0F #define CNTR2 0x0F #define RDMAPORT 0X10 #define RSTPORT 0x18 //****************************************************************** //* RTL8019AS INITIAL REGISTER VALUES //****************************************************************** #define rcrval 0x04 #define tcrval 0x00 #define dcrval 0x58 // was 0x48 #define imrval 0x11 // PRX and OVW interrupt enabled #define txstart 0x40 #define rxstart 0x46 #define rxstop 0x60 //****************************************************************** //* RTL8019AS 9346 EEPROM PIN DEFINITIONS //****************************************************************** #define cregaddr PORTB #define cregdata PORTD #define tocreg set_tris_D(0x00); #define fromcreg set_tris_D(0xFF); //****************************************************************** //* RTL8019AS 9346 EEPROM PIN DEFINITIONS //****************************************************************** #define EESK PORTB,5 #define EEDI PORTB,6 #define EEDO PORTA,0 //****************************************************************** //* RTL8016AS PIN DEFINITIONS //****************************************************************** #define INT0 PORTC,0 #define ior_pin PORTE,0 #define iow_pin PORTE,1 #define rst_pin PORTE,2 //****************************************************************** //* RTL8016AS ISR REGISTER DEFINITIONS //****************************************************************** #define RST 0x07 #define RDC 0x06 #define OVW 0x04 #define PRX 0x00 //****************************************************************** //* PIC16F87X RAM Definitions //****************************************************************** unsigned int8 aux_data[16]; //received data area int8 *addr; int8 byte_read,data_H,data_L; int8 high_nibble, low_nibble, high_char, low_char,resend; int16 i,txlen,rxlen,chksum16,hdrlen,tcplen,tcpdatalen_in; int16 tcpdatalen_out,ISN,portaddr,ip_packet_len; int32 hdr_chksum,my_seqnum,client_seqnum,incoming_ack,expected_ack; short synflag,finflag; #define set_packet32(d,s) packet[d] = make8(s,3); \ packet[d+1] = make8(s,2); \ packet[d+2] = make8(s,1); \ packet[d+3]= make8(s,0); \ //****************************************************************** //* Application Code //* Your application code goes here. //* This particular code toggles the LED on PORT A bit 4 using //* Telnet. //****************************************************************** #separate void application_code() { if(aux_data[0] != 0x0A) tcpdatalen_out = tcpdatalen_in; if(aux_data[0] == 0x0A) tcpdatalen_out = 0x00; if(aux_data[0] == '0') bit_set(PORTA,4); if (aux_data[0] == '1') bit_clear(PORTA,4); if (aux_data[0] == 0x0D) { strcpy(packet[TCP_data],"\r\nEDTP Telnet Server>"); tcpdatalen_out = 21; } } //****************************************************************** //* Perform ARP Response //* This routine supplies a requesting computer with the //* Ethernet modules's MAC (hardware) address. //****************************************************************** void arp() { //start the NIC write_creg(CR,0x22); //load beginning page for transmit buffer write_creg(TPSR,txstart); //set start address for remote DMA operation write_creg(RSAR0,0x00); write_creg(RSAR1,0x40); //clear the Interrupts write_creg(ISR,0xFF); //load data byte count for remote DMA write_creg(RBCR0,0x3C); write_creg(RBCR1,0x00); //do remote write operation write_creg(CR,0x12); //write destination MAC address for(i=0;i<6;++i) write_creg(RDMAPORT,packet[enetpacketSrc0+i]); //write source MAC address for(i=0;i<6;++i) write_creg(RDMAPORT,MYMAC[i]); //write typelen hwtype prtype hwlen prlen op: addr = &packet[enetpacketType0]; packet[arp_op+1] = 0x02; for(i=0;i<10;++i) write_creg(RDMAPORT,*addr++); //write ethernet module MAC address for(i=0;i<6;++i) write_creg(RDMAPORT,MYMAC[i]); //write ethernet module IP address for(i=0;i<4;++i) write_creg(RDMAPORT,MYIP[i]); //write remote MAC address for(i=0;i<6;++i) write_creg(RDMAPORT,packet[enetpacketSrc0+i]); //write remote IP address for(i=0;i<4;++i) write_creg(RDMAPORT,packet[arp_sipaddr+i]); //write some pad characters to fill out the packet to //the minimum length for(i=0;i<0x12;++i) write_creg(RDMAPORT,0x00); //make sure the DMA operation has successfully completed byte_read = 0; while(!bit_test(byte_read,RDC)) read_creg(ISR); //load number of bytes to be transmitted write_creg(TBCR0,0x3C); write_creg(TBCR1,0x00); //send the contents of the transmit buffer onto the network write_creg(CR,0x24); } //****************************************************************** //* Perform ICMP Function //* This routine responds to a ping. //****************************************************************** void icmp() { //set echo reply packet[ICMP_type]=0x00; packet[ICMP_code]=0x00; //clear the ICMP checksum packet[ICMP_cksum ]=0x00; packet[ICMP_cksum+1]=0x00; //setup the IP header setipaddrs(); //calculate the ICMP checksum hdr_chksum =0; hdrlen = (make16(packet[ip_pktlen],packet[ip_pktlen+1])) - ((packet[ip_vers_len] & 0x0F) * 4); addr = &packet[ICMP_type]; cksum(); chksum16= ~(hdr_chksum + ((hdr_chksum & 0xFFFF0000) >> 16)); packet[ICMP_cksum] = make8(chksum16,1); packet[ICMP_cksum+1] = make8(chksum16,0); //send the ICMP packet along on its way echo_packet(); } //****************************************************************** //* UDP Function //* This function uses a Visual Basic UDP program to echo the //* data back to the VB program and set or reset PORT A bit 4 //* under control of the VB program. //****************************************************************** void udp() { //port 7 is the well-known echo port if(packet[UDP_destport] == 0x00 && packet[UDP_destport+1] ==0x07) { //build the IP header setipaddrs(); //swap the UDP source and destination ports data_L = packet[UDP_srcport]; packet[UDP_srcport] = packet[UDP_destport]; packet[UDP_destport] = data_L; data_L = packet[UDP_srcport+1]; packet[UDP_srcport+1] = packet[UDP_destport+1]; packet[UDP_destport+1] = data_L; //calculate the UDP checksum packet[UDP_cksum] = 0x00; packet[UDP_cksum+1] = 0x00; hdr_chksum =0; hdrlen = 0x08; addr = &packet[ip_srcaddr]; cksum(); hdr_chksum = hdr_chksum + packet[ip_proto]; hdrlen = 0x02; addr = &packet[UDP_len]; cksum(); hdrlen = make16(packet[UDP_len],packet[UDP_len+1]); addr = &packet[UDP_srcport]; cksum(); chksum16= ~(hdr_chksum + ((hdr_chksum & 0xFFFF0000) >> 16)); packet[UDP_cksum] = make8(chksum16,1); packet[UDP_cksum+1] = make8(chksum16,0); //echo the incoming data back to the VB program echo_packet(); } //buttons on the VB GUI are pointed towards port address 5000 decimal else if(packet[UDP_destport] == 0x13 && packet[UDP_destport+1] == 0x88); { if(packet[UDP_data]) //received a 0x00 from the VB program bit_set(PORTA,4); else //received a 0xFF from the VB program bit_clear(PORTA,4); } } //****************************************************************** //* TCP Function //* This function uses TCP protocol to act as a Telnet server on //* port 8088 decimal. The application function is called with //* every incoming character. //****************************************************************** #separate void tcp() { //assemble the destination port address from the incoming packet portaddr = make16(packet[TCP_destport],packet[TCP_destport+1]); //calculate the length of the data coming in with the packet //tcpdatalen_in = incoming packet length - incoming ip header length - incoming tcp header length tcpdatalen_in = (make16(packet[ip_pktlen],packet[ip_pktlen+1])) - ((packet[ip_vers_len] & 0x0F) * 4) - (((packet[TCP_hdrflags] & 0xF0) >> 4) * 4); //If an ACK is recieved and the destination port address is valid and no data is in the packet if(ACK_IN && portaddr == MY_PORT_ADDRESS && tcpdatalen_in == 0x00) { //assemble the acknowledgment number from the incoming packet incoming_ack =make32(packet[TCP_acknum],packet[TCP_acknum+1],packet[TCP_acknum+2],packet[TCP_acknum+3]); //if the incoming packet is a result of session establishment if(synflag) { //clear the SYN flag synflag = 0; //the incoming acknowledgment is my new sequence number my_seqnum = incoming_ack; //send the Telnet server banner //limit the character count to 40 decimal strcpy(packet[TCP_data],"EASY ETHERNET\r\n1=LED ON\r\n0=LED OFF\r\n>"); //length of the banner message tcpdatalen_out = 37; //expect to get an acknowledgment of the banner message expected_ack = my_seqnum +tcpdatalen_out; //send the TCP/IP packet send_tcp_packet(); } } //if an ack is received and the port address is valid and there is data in the incoming packet if(ACK_IN && portaddr == MY_PORT_ADDRESS && tcpdatalen_in) { for(i=0;i> 16)); packet[TCP_cksum] = make8(chksum16,1); packet[TCP_cksum+1] = make8(chksum16,0); echo_packet(); } //this code segment processes a FIN from the Telnet client //and acknowledges the FIN and any incoming data. if(FIN_IN && portaddr == MY_PORT_ADDRESS) { if(tcpdatalen_in) { for(i=0;i> 16)); packet[TCP_cksum] = make8(chksum16,1); packet[TCP_cksum+1] = make8(chksum16,0); txlen = ip_packet_len + 14; if(txlen < 60) txlen = 60; data_L = make8(txlen,0); data_H = make8(txlen,1); write_creg(CR,0x22); write_creg(TPSR,txstart); write_creg(RSAR0,0x00); write_creg(RSAR1,0x40); write_creg(ISR,0xFF); write_creg(RBCR0,data_L); write_creg(RBCR1,data_H); write_creg(CR,0x12); for(i=0;i 0x09) high_char = high_nibble + 0x37; else high_char = high_nibble + 0x30; low_nibble = (binchar & 0x0F); if(low_nibble > 0x09) low_char = low_nibble + 0x37; else low_char = low_nibble + 0x30; } //****************************************************************** //* Used with Tera Term to clear the screen (VT-100 command) //****************************************************************** #separate void cls(void) { printf("%c[2J",esc); } //****************************************************************** //* show_packet //* This routine is for diagnostic purposes and displays //* the Packet Buffer memory in the PIC. //****************************************************************** #separate void show_packet() { cls(); printf("\r\n"); data_L = 0x00; for(i=0;i<96;++i) { bin2hex(packet[i]); printf(" %c%c",high_char,low_char); if(++data_L == 0x10) { data_L = 0x00; printf("\r\n"); } } } //****************************************************************** //* show_aux_packet //* This routine is a diagnostic that displays Auxillary //* Packet Buffer buffer memory in the PIC. //****************************************************************** #separate void show_aux_packet() { cls(); printf("\r\n"); data_L = 0x00; for(i=0;i<80;++i) { bin2hex(aux_data[i]); printf(" %c%c",high_char,low_char); if(++data_L == 0x10) { data_L = 0x00; printf("\r\n"); } } } //****************************************************************** //* Write to NIC Control Register //****************************************************************** void write_creg(int regaddr, int regdata) { cregaddr = regaddr; cregdata = regdata; tocreg; bit_clear(iow_pin); delay_cycles(1); bit_set(iow_pin); fromcreg; } //****************************************************************** //* Read From NIC Control Register //****************************************************************** int8 read_creg(int regaddr) { fromcreg; cregaddr = regaddr; bit_clear(ior_pin); byte_read = input_d(); bit_set(ior_pin); return(byte_read); } //****************************************************************** //* Detect EEPROM Clock from RTL8019AS //****************************************************************** void clock_9346() { #asm chk_is_lo: btfss EESK goto chk_is_lo chk_is_hi: btfsc EESK goto chk_is_hi #endasm } //****************************************************************** //* Emulate the presence of a 9346 EEPROM //****************************************************************** void fakeout_9346() { int8 reps,clocks,datum; #asm movlw 0x02 movwf reps reload: bcf EEDO movlw 0x03 //0x03=half duplex 0x07=full duplex movwf datum #endasm start_bit: clock_9346(); #asm btfss EEDI goto start_bit #endasm clock_9346(); #asm btfss EEDI goto start_bit #endasm clock_9346(); #asm btfsc EEDI goto start_bit movlw 0x06 movwf clocks #endasm addr: clock_9346(); #asm decfsz clocks,1 goto addr movlw 0x10 movwf clocks bcf C #endasm send: clock_9346(); #asm movf datum,0 movwf PORTA rrf datum,1 decfsz clocks,1 goto send decfsz reps,1 goto reload bcf EEDO #endasm } //****************************************************************** //* Handle Receive Ring Buffer Overrun //* No packets are recovered //****************************************************************** void overrun() { read_creg(CR); data_L = byte_read; write_creg(CR,0x21); delay_ms(2); write_creg(RBCR0,0x00); write_creg(RBCR1,0x00); if(!bit_test(data_L,2)) resend = 0; else if(bit_test(data_L,2)) { read_creg(ISR); data_L = byte_read; if(bit_test(data_L,1) || bit_test(data_L,3)) resend = 0; else resend = 1; } write_creg(TCR,0x02); write_creg(CR,0x22); write_creg(BNRY,rxstart); write_creg(CR,0x62); write_creg(CURR,rxstart); write_creg(CR,0x22); write_creg(ISR,0x10); write_creg(TCR,tcrval); } //****************************************************************** //* Echo Packet Function //* This routine does not modify the incoming packet size and //* thus echoes the original packet structure. //****************************************************************** void echo_packet() { write_creg(CR,0x22); write_creg(TPSR,txstart); write_creg(RSAR0,0x00); write_creg(RSAR1,0x40); write_creg(ISR,0xFF); write_creg(RBCR0,pageheader[enetpacketLenL] - 4 ); write_creg(RBCR1,pageheader[enetpacketLenH]); write_creg(CR,0x12); txlen = make16(pageheader[enetpacketLenH],pageheader[enetpacketLenL]) - 4; for(i=0;i> 16)); packet[ip_hdr_cksum] = make8(chksum16,1); packet[ip_hdr_cksum+1] = make8(chksum16,0); } //****************************************************************** //* CHECKSUM CALCULATION ROUTINE //****************************************************************** void cksum() { while(hdrlen > 1) { data_H=*addr++; data_L=*addr++; chksum16=make16(data_H,data_L); hdr_chksum = hdr_chksum + chksum16; hdrlen -=2; } if(hdrlen > 0) { data_H=*addr; data_L=0x00; chksum16=make16(data_H,data_L); hdr_chksum = hdr_chksum + chksum16; } } //****************************************************************** //* Initialize the RTL8019AS //****************************************************************** void init_RTL8019AS() { ADCON1 = 0x06; //00000110 all digital to start ADCON0 = 0; set_tris_C(0x85); set_tris_A(0x00); bit_clear(EEDO); set_tris_B(0xE0); // setup address lines cregaddr = 0x00; // clear address lines fromcreg; // address lines = input set_tris_E(0x00); // setup IOW, IOR, RESET bit_set(iow_pin); // disable IOW bit_set(ior_pin); // disable IOR bit_set(rst_pin); // put NIC in reset delay_ms(2); // delay at least 1.6ms bit_clear(rst_pin); // disable reset line read_creg(RSTPORT); // read contents of reset port write_creg(RSTPORT,byte_read); // do soft reset delay_ms(10); // give it time read_creg(ISR); // check for good soft reset if(!bit_test(byte_read,RST)){ while(1){ printf("INIT FAILED\n\r"); } } write_creg(CR,0x21); // stop the NIC, abort DMA, page 0 delay_ms(2); // make sure nothing is coming in or going out write_creg(DCR,dcrval); // 0x58 write_creg(RBCR0,0x00); write_creg(RBCR1,0x00); write_creg(RCR,0x04); write_creg(TPSR,txstart); write_creg(TCR,0x02); write_creg(PSTART,rxstart); write_creg(BNRY,rxstart); write_creg(PSTOP,rxstop); write_creg(CR,0x61); delay_ms(2); write_creg(CURR,rxstart); for(i=0;i<6;++i) write_creg(PAR0+i, MYMAC[i]); write_creg(CR,0xC1); write_creg(CR9346,0xC0); write_creg(CR9346,0x40); fakeout_9346(); delay_ms(10); write_creg(CR,0x21); write_creg(DCR,dcrval); write_creg(CR,0x22); write_creg(ISR,0xFF); write_creg(IMR,imrval); write_creg(TCR,tcrval); } //****************************************************************** //* MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN //****************************************************************** void main() { init_RTL8019AS(); synflag = 0; finflag = 0; //****************************************************************** //* Look for a packet in the receive buffer ring //****************************************************************** while(1) { //start the NIC write_creg(CR,0x22); //wait for a good packet while(!bit_test(INT0)); //read the interrupt status register read_creg(ISR); //if the receive buffer has been overrun if(bit_test(byte_read,OVW)) overrun(); //if the receive buffer holds a good packet if(bit_test(byte_read,PRX)) get_packet(); //make sure the receive buffer ring is empty //if BNRY = CURR, the buffer is empty read_creg(BNRY); data_L = byte_read; write_creg(CR,0x62); read_creg(CURR); data_H = byte_read; write_creg(CR,0x22); //buffer is not empty.. get next packet if(data_L != data_H) get_packet(); //reset the interrupt bits write_creg(ISR,0xFF); } }