I intend to ignore the slip driver. Source for the ethernet driver is in usr/src/sys/hp300/dev in the files if_le.c and if_lereg.h
This is the AMD AM7990 chip. I still have the 1994 AMD Ethernet family databook on my shelf and it describes the AM79C90 chip which they call the "Local Area Network Controller for Ethernet", which is the origin of the name "LANCE".
The driver is a bit misleading. It calls the actual lance hardware registers "lereg1". The hp300 board apparently has some registers it calls lereg0, and the driver calls control structures in ram "lereg2". A fourth area of memory is NVRAM, from which it gets the ethernet address.
It looks like once the driver has received a packet, it calls "ether_input" to hand it up to the IP layer. This happens in the leread() routine. The routine ether_input() is in net/if_ethersubr.c Using grep to search, it looks like each driver typically makes a single call to ether_input() when it receives packets.
It looks like leput() gets called to give the driver a packet to transmit. However this is just a call internal to the driver (may as well be static). The routine leput() gets called by lestart() and this routine loops pulling packets off of the "if_snd" queue and passing them to leput().
The routine ether_output() is in net/if_ethersubr.c right alongside of ether_input(). This puts the packet on the if_snd queue and calls the start function if appropriate.
Searching for calls to ether_output is surprisingly more complex. Each driver places a pointer to ether_output into the if_output element of its if_net structure. This is used to make a call in 2 places in the routine ip_output() (in netinet/ip_output.c). It is also used in netinet/if_ether.c in the ARP handling code (3 calls).
Searching on ipintrq takes us to ip_input.c We could view things so far as 2 layers, maybe 3. First we have the device driver layer. Second we have the generic ethernet layer. Now we have the IP layer.
The routine ipintr() pulls just one packet off of ipintrq and processes it. It performs a lot of validation, then finally uses a table of protocol switch structures to hand it to the proper protocol input routine. it calls:
(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);This is initialized in ip_proto.c and for TCP the entry looks like:
{ SOCK_STREAM, &inetdomain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD, tcp_input, 0, tcp_ctlinput, tcp_ctloutput, tcp_usrreq, tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,The packet gets passed to tcp_input() which is in tcp_input.c
The TCP code totals to 4448 lines as follows:
[tom@trona netinet]$ wc tcp* 159 605 4529 tcp_debug.c 59 352 2305 tcp_debug.h 85 540 3595 tcp_fsm.h 98 541 3639 tcp.h 1647 6745 47086 tcp_input.c 59 375 2524 tcpip.h 599 2636 17475 tcp_output.c 62 409 2652 tcp_seq.h 445 1853 12735 tcp_subr.c 312 1317 9099 tcp_timer.c 128 924 5734 tcp_timer.h 517 1816 12645 tcp_usrreq.c 278 1826 12088 tcp_var.h 4448 19939 136106 total
There is a missing third player in the TCP game, and that is interaction with the socket layer. How do reads and writes take place? What sorts of things besides reads and writes do we need to consider? Calls to bind, listen, accept, and socket options come to mind.
All of that however is a topic for another page ...
Kyu / [email protected]