mirror of
https://github.com/Zeal-Operating-System/ZealOS.git
synced 2025-06-07 08:14:48 +00:00
added more Net code still WIP, included Tom palettes, hasty alteration to 3D model editor slider to allow higher precision.
This commit is contained in:
parent
c7a04c4ee7
commit
7e6e442e0d
BIN
Zenith-latest-2020-03-20-19_32_59.iso → Zenith-latest-2020-03-27-14_00_22.iso
Normal file → Executable file
BIN
Zenith-latest-2020-03-20-19_32_59.iso → Zenith-latest-2020-03-27-14_00_22.iso
Normal file → Executable file
Binary file not shown.
0
src/Home/MakeHome.CC
Normal file → Executable file
0
src/Home/MakeHome.CC
Normal file → Executable file
@ -56,6 +56,7 @@ I64 ARPSend(U16 operation,
|
||||
{//method currently assumes send_ and target_ip EndianU16 already...
|
||||
|
||||
U8* ethernet_frame;
|
||||
CARPHeader *header;
|
||||
I64 de_index = EthernetFrameAllocate(ðernet_frame,
|
||||
send_mac_address,
|
||||
dest_mac_address,
|
||||
@ -63,7 +64,7 @@ I64 ARPSend(U16 operation,
|
||||
sizeof(CARPHeader));
|
||||
if (de_index < 0) return de_index;// error state
|
||||
|
||||
CARPHeader *header = ethernet_frame;
|
||||
header = ethernet_frame;
|
||||
|
||||
header->hardware_type = EndianU16(HTYPE_ETHERNET);
|
||||
header->protocol_type = EndianU16(ETHERTYPE_IPV4);
|
||||
@ -83,6 +84,9 @@ I64 ARPSend(U16 operation,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
CARPHash *ARPCacheFindByIP(U32 ip_address)
|
||||
{
|
||||
U8 *ip_string = MStrPrint("%d", ip_address);
|
||||
@ -183,9 +187,10 @@ I64 ARPHandler(CEthernetFrame *ethernet_frame)
|
||||
if (header->target_protocol_addr == arp_globals.local_ipv4)
|
||||
ARPSend(ARP_REPLY,
|
||||
header->sender_hardware_addr,
|
||||
EthernetGetMAC,
|
||||
EthernetGetMAC(),
|
||||
arp_globals.local_ipv4,
|
||||
header->sender_hardware_addr, header->sender_protocol_addr);
|
||||
header->sender_hardware_addr,
|
||||
header->sender_protocol_addr);
|
||||
break;
|
||||
case ARP_REPLY:
|
||||
ARPCachePut(EndianU32(header->sender_protocol_addr), header->sender_hardware_addr);
|
||||
|
@ -36,7 +36,7 @@ NetQueue
|
||||
|
||||
|
||||
Ethernet
|
||||
EthernetFrameParse (Needs refining!)
|
||||
EthernetFrameParse (has a fixme)
|
||||
|
||||
|
||||
ARP
|
||||
@ -48,14 +48,31 @@ ARP
|
||||
ARPSetIPV4Address
|
||||
|
||||
|
||||
Sockets
|
||||
|
||||
Sockets (just finite state modifiers.)
|
||||
SocketStateErr
|
||||
SocketAccept
|
||||
SocketClose
|
||||
SocketBind
|
||||
SocketConnect
|
||||
SocketListen
|
||||
SocketReceive
|
||||
SocketReceiveFrom
|
||||
SocketSend
|
||||
SocketSendTo
|
||||
|
||||
IPV4
|
||||
|
||||
IPV4Checksum
|
||||
GetMACAddressForIP
|
||||
IPV4PacketAllocate
|
||||
IPV4PacketFinish (alias for EthernetFrameFinish)
|
||||
IPV4GetAddress
|
||||
IPV4SetAddress
|
||||
IPV4SetSubnet
|
||||
IPV4ParsePacket
|
||||
|
||||
ICMP
|
||||
|
||||
ICMPSendReply
|
||||
ICMPHandler
|
||||
|
||||
TCP
|
||||
|
||||
@ -69,5 +86,6 @@ DNS
|
||||
NetHandlerTask
|
||||
NetHandlerTask
|
||||
HandleNetQueueEntry (4 million context swaps per second!)
|
||||
IPV4Handler
|
||||
|
||||
NetConfig
|
@ -11,6 +11,8 @@ Departures from Shrine:
|
||||
|
||||
Many file global vars have been condensed into global classes.
|
||||
|
||||
Sockets are non-standard.
|
||||
|
||||
Stack progress: (# done, ~ WIP, . N/A)
|
||||
|
||||
# PCNet-II Driver
|
||||
@ -18,21 +20,27 @@ Stack progress: (# done, ~ WIP, . N/A)
|
||||
# NetQueue
|
||||
- no NetHandler currently
|
||||
|
||||
~ Ethernet
|
||||
- need frame parse method
|
||||
# Ethernet
|
||||
- double check.
|
||||
|
||||
# ARP (Address Resolution Protocol)
|
||||
- double check.
|
||||
|
||||
. Sockets
|
||||
- thinking I should nix Sockets and
|
||||
work out a STREAMS protocol instead.
|
||||
Need more docs on STREAMS.
|
||||
~ Sockets
|
||||
- Implemented a Finite State Machine
|
||||
through Socket function calls.
|
||||
Sockets themselves do nothing,
|
||||
all calls simply do/don't alter
|
||||
socket state. Protocols will
|
||||
need to detect socket states
|
||||
and respond appropriately.
|
||||
|
||||
|
||||
. IPV4 (Internet Protocol version 4)
|
||||
# IPV4 (Internet Protocol version 4)
|
||||
- double check, some FIXMEs.
|
||||
|
||||
. ICMP (Internet Control Message Protocol)
|
||||
# ICMP (Internet Control Message Protocol)
|
||||
- ? has FIXMEs.
|
||||
|
||||
. TCP (Transmission Control Protocol)
|
||||
|
||||
@ -53,4 +61,13 @@ Stack progress: (# done, ~ WIP, . N/A)
|
||||
made so we can just do a switch (ethertype) and
|
||||
then directly call the appropriate handler.
|
||||
|
||||
- As of ICMP, this seems fine to do. IPV4 Handler needed
|
||||
to do something very similar to what NetHandler does,
|
||||
so for simplicity IPV4Handler was moved to NetHandler
|
||||
file, as it does a switch with protocol types that
|
||||
are defined in files after IPV4.CC is included. This should
|
||||
likely be the convention for Handlers that need to process
|
||||
data from an all-knowing perspective (i.e. NetHandler file is
|
||||
included last.)
|
||||
|
||||
. NetConfig
|
BIN
src/Home/Net/Docs/SocketMapping.DD
Executable file
BIN
src/Home/Net/Docs/SocketMapping.DD
Executable file
Binary file not shown.
182
src/Home/Net/Docs/SocketNotes.DD
Executable file
182
src/Home/Net/Docs/SocketNotes.DD
Executable file
@ -0,0 +1,182 @@
|
||||
|
||||
Sockets... not planning to conform to unix-y socket standards unless absolutely required.
|
||||
All over socket code is these global vars, lowercase functions.. its a mess.
|
||||
|
||||
Shrine does some pretty gnarly stuff to do their NativeSockets.
|
||||
|
||||
They have a class, called a CSocket, which is just a bunch of function pointers.
|
||||
|
||||
Then, they have another, called CSocketClass, which is a single direction list of
|
||||
a domain, a type, some padding (...), and a pointer to a CSocket..
|
||||
|
||||
I don't even want to get into the CAddrResolver which is a yet another
|
||||
function pointer to some resolver function.
|
||||
|
||||
Then, the socketclass and addrresolver are made into globals!
|
||||
|
||||
When they try to find a socket class, they have to loop through all
|
||||
of the defined socket classes until they find one that matches the
|
||||
params of domain and type..
|
||||
|
||||
|
||||
Since UDP is regarded apparently as more simple than TCP,
|
||||
looking at Shrine's UDP code gives a little insight into
|
||||
what's going on without getting too caught up in high
|
||||
level intricacies.
|
||||
|
||||
When UDP registers their socketclass , they pass in the #defined
|
||||
type and domain, then pass in the socketclass itself.
|
||||
|
||||
All the typical socket functions as defined in shrine,
|
||||
basically just take in this socketclass and then redirect
|
||||
execution to the function defined in the socketclass.
|
||||
|
||||
|
||||
|
||||
At first, I thought "oh, why not just make the args a class!" but
|
||||
this doesn't address the need to store functions as members, which
|
||||
is a little niggerlicious still.
|
||||
|
||||
So I need a better way to do these socket function calls, without
|
||||
having to rely on a pointer magic backend. The immediate thought
|
||||
I have, is to ditch general use socket functions and enforce
|
||||
application specific function definitions. In a way, that might
|
||||
kinda end up meaning that a hell of a lot of the other way
|
||||
of doing things would be stripped.
|
||||
|
||||
Maybe, analyze what passed variables are manipulated, and hardcode
|
||||
functions with higher specificity.
|
||||
|
||||
|
||||
...
|
||||
|
||||
/* Zenith Sockets are non-standard.
|
||||
|
||||
----------------------
|
||||
Shrine implementation:
|
||||
|
||||
in_addr
|
||||
sockaddr
|
||||
sockaddr_in
|
||||
addrinfo
|
||||
|
||||
inet_aton
|
||||
inet_ntoa
|
||||
|
||||
socket
|
||||
accept
|
||||
close
|
||||
bind
|
||||
connect
|
||||
listen
|
||||
recv
|
||||
recvfrom
|
||||
send
|
||||
sendto
|
||||
setsockopt
|
||||
getaddrinfo
|
||||
freeaddrinfo
|
||||
|
||||
AddrInfoCopy
|
||||
gai_strerror
|
||||
create_connection
|
||||
RegisterSocketClass
|
||||
----------------------
|
||||
Zenith implementation:
|
||||
|
||||
|
||||
|
||||
----------------------
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* I think a class for a socket is a good idea.
|
||||
But i do not think that the class should have
|
||||
function pointers.
|
||||
|
||||
A CSocket is literally just a list of function pointers.
|
||||
|
||||
In Shrine, for example, the only UDP socket functions
|
||||
that actually do anything are UDPSocketBind, UDPSocketClose,
|
||||
UDPSocketRecvFrom, UDPSocketSendTo, and UDPSocketSetSockopt.
|
||||
The rest just make the compiler happy by saying no_warn on the
|
||||
args ...
|
||||
|
||||
If not function pointers, how would it work?
|
||||
|
||||
STREAMS / XTI / TPI lookin sexy rn ngl haha.
|
||||
|
||||
Maybe a hybrid of the alternatives and sockets.
|
||||
|
||||
One thought is to reserve sockets as some unique class thing,
|
||||
and have functions that take args that go to a switch statement
|
||||
to determine which code to next execute.
|
||||
|
||||
Perhaps, two Socket related files would make more sense,
|
||||
one which defines some low-level things, and another that
|
||||
(like NetHandler) is all-knowing and would discern based
|
||||
on a switch statement which socket-related functions to run.
|
||||
If doing this, must make sure that UDP/TCP/etc won't
|
||||
need to know things only the SocketHandler or whatever we'd
|
||||
call that would know.
|
||||
|
||||
At the root of ShrineSockets, all sockets made in later
|
||||
files must be put into a RegisterSocketClass call,
|
||||
which keeps track of which functions go where based on
|
||||
the searched-for domain and type. So, I'd think a
|
||||
beginning to an unfuck would be, if it ends up needed,
|
||||
using a hash table, maybe the key would be (domain << 64 | type)
|
||||
as a string, assuming the args are still I64.
|
||||
|
||||
I fight and I fight.. these bold ideas might be forced
|
||||
to take place only after I've un-fucked sockets code alone.
|
||||
Then maybe after massive unfucking/refucking, it can be more
|
||||
quickly discerened what the better way to go from there is.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
//at the end of the day , one distinction MUST be made...
|
||||
//What IS a socket ? ...
|
||||
|
||||
....
|
||||
|
||||
|
||||
---------------------
|
||||
|
||||
Thoughts.
|
||||
|
||||
|
||||
Sense in SocketNextState(CSocket *socket, U8 state) ?
|
||||
|
||||
Sense in having two socket states, current and requested?
|
||||
With one, if we try to modify the value, we modify it.
|
||||
There's no way of conveying or interacting with
|
||||
higher-defined (TCP, UDP, etc) socket functions.
|
||||
With a current and requested state, we would be
|
||||
able to show both what the socket is doing right
|
||||
now, and what the user/code has requested the
|
||||
socket to change to. Say, there is a failure
|
||||
in the higher level code when it sees a socket
|
||||
and its requested next state: it will process
|
||||
appropriate higher-level code and then ask for
|
||||
another Socket State change accordingly or something.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
BIN
src/Home/Net/Docs/StrToAddressFSM.DD
Executable file
BIN
src/Home/Net/Docs/StrToAddressFSM.DD
Executable file
Binary file not shown.
43
src/Home/Net/Docs/UDPTreeNotes.DD
Executable file
43
src/Home/Net/Docs/UDPTreeNotes.DD
Executable file
@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
|
||||
$FG,0$So. We don't have any way to REMOVE or POP treenodes or treequeues.
|
||||
|
||||
Goal?
|
||||
- CUDPTreeNode* UDPTreeNodePop (args?)
|
||||
|
||||
- U0 UDPTreeNodeFree(CUDPTreeNode *node)
|
||||
|
||||
|
||||
|
||||
- CUDPTreeQueue* UDPTreeNodeQueuePop (args?)
|
||||
|
||||
- U0 UDPTreeNodeQueueFree(CUDPTreeQueue *queue)
|
||||
|
||||
|
||||
Pop will give you the now-popped-out node pointer.
|
||||
Free will take that popped node and Free its resources from mem.
|
||||
|
||||
|
||||
To POP a node, different steps necessary.
|
||||
If node has no left/right, it's simple. Just free it all.
|
||||
|
||||
If node has only left or only right, pop it by storing its
|
||||
pointer for returning, then altering the left/right pointers
|
||||
for the above tree, and below tree, to stitch it one link closer
|
||||
|
||||
If node has both a left and right branch...
|
||||
Well, i dunno. harder to stitch because conflicting trees.
|
||||
Hail-mary approach is to take all the branches and respective
|
||||
branches and just filter back into tree with Add.
|
||||
|
||||
It sounds painful.
|
||||
|
||||
Is it feasible to just take the Whole sub-Trees of a node,
|
||||
cut ties between node and tree, then add back in the left,
|
||||
then right sub trees? If we're getting rid of a central node,
|
||||
that splits off into higher or lowers... ultimately, if we sever
|
||||
the original node, either direction is still going to replace
|
||||
the position of where the original node was, because it will
|
||||
still be higher/lower than the higher-up-node. Adding back in
|
||||
nodes would just leave the tree a little funky-lookin.
|
BIN
src/Home/Net/Docs/ZenithStackNotes.DD
Executable file
BIN
src/Home/Net/Docs/ZenithStackNotes.DD
Executable file
Binary file not shown.
@ -29,7 +29,7 @@ U0 EthernetInitGlobals()
|
||||
|
||||
|
||||
|
||||
//This method is retarded. It needs help.
|
||||
//todo: check length , figure out the length+4
|
||||
U0 EthernetFrameParse(CEthernetFrame *frame_out, U8 *frame, U16 length)
|
||||
{
|
||||
//Shrine has a FIXME for check length! We need to figure out what
|
||||
@ -38,15 +38,15 @@ U0 EthernetFrameParse(CEthernetFrame *frame_out, U8 *frame, U16 length)
|
||||
//of the current system should be done with less extra allocation
|
||||
//altogether, more passing.
|
||||
|
||||
MemCopy(frame_out->destination_address, frame, 6); // 6 ? Why 6? Need a #define...
|
||||
MemCopy(frame_out->destination_address, frame, MAC_ADDRESS_LENGTH);
|
||||
|
||||
MemCopy(frame_out->source_address, frame + 6, 6); // Ridiculous, what are those
|
||||
MemCopy(frame_out->source_address, frame + MAC_ADDRESS_LENGTH, MAC_ADDRESS_LENGTH);
|
||||
|
||||
frame_out->ethertype = frame[13] | (frame[12] << 8); // This would be readable with #defines
|
||||
frame_out->ethertype = frame[ETHERNET_ETHERTYPE_OFFSET+1] | (frame[ETHERNET_ETHERTYPE_OFFSET] << 8);
|
||||
|
||||
frame_out->data = frame + 14; // Fuck you Shrine
|
||||
frame_out->data = frame + ETHERNET_DATA_OFFSET;
|
||||
|
||||
frame_out->length = length - 14 + 4; //... He has a comment literally just saying "??". Wow.
|
||||
frame_out->length = length - ETHERNET_MAC_HEADER_LENGTH + 4; //... He has a comment literally just saying "??". Wow.
|
||||
}
|
||||
|
||||
EthernetInitGlobals;
|
83
src/Home/Net/ICMP.CC
Executable file
83
src/Home/Net/ICMP.CC
Executable file
@ -0,0 +1,83 @@
|
||||
#include "IPV4"
|
||||
|
||||
#define ICMP_TYPE_ECHO_REPLY 0
|
||||
#define ICMP_TYPE_ECHO_REQUEST 8
|
||||
|
||||
class CICMPHeader // Shrine's use of id and seq indicate this header is for Address Mask Reply/Request
|
||||
{
|
||||
U8 type;
|
||||
U8 code;
|
||||
U16 checksum;
|
||||
|
||||
U16 identifier;
|
||||
U16 sequence_number;
|
||||
};
|
||||
|
||||
|
||||
I64 ICMPSendReply(U32 destination_ip_address,
|
||||
U16 identifier,
|
||||
U16 sequence_number,
|
||||
U16 request_checksum,
|
||||
U8 *payload,
|
||||
I64 length)
|
||||
{
|
||||
U8 *frame;
|
||||
I64 de_index;
|
||||
CICMPHeader *header;
|
||||
|
||||
de_index = IPV4PacketAllocate(&frame,
|
||||
IP_PROTOCOL_ICMP,
|
||||
IPV4GetAddress(),
|
||||
destination_ip_address,
|
||||
sizeof(CICMPHeader) + length);
|
||||
if (de_index < 0)
|
||||
{
|
||||
ZenithLog("ICMP Send Reply failed to allocate IPV4 packet.\n");
|
||||
return de_index;
|
||||
}
|
||||
|
||||
header = frame;
|
||||
|
||||
|
||||
header->type = ICMP_TYPE_ECHO_REPLY;
|
||||
|
||||
header->code = 0; // why is 0 okay?
|
||||
|
||||
header->checksum = EndianU16(EndianU16(request_checksum) + 0x0800);// this is awful. Shrine says hack alert.
|
||||
|
||||
header->identifier = identifier;
|
||||
|
||||
header->sequence_number = sequence_number;
|
||||
|
||||
MemCopy(frame + sizeof(CICMPHeader), payload, length);
|
||||
|
||||
IPV4PacketFinish(de_index);
|
||||
//return IPV4PacketFinish
|
||||
}
|
||||
|
||||
I64 ICMPHandler(CIPV4Packet *packet)
|
||||
{
|
||||
CICMPHeader *header;
|
||||
|
||||
if (packet->length < sizeof(CICMPHeader))
|
||||
{
|
||||
ZenithLog("ICMP Handler caught wrong IPV4 length.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
header = packet->data;
|
||||
|
||||
if (header->type == ICMP_TYPE_ECHO_REQUEST && header->code == 0)//why zero? need more #defines in here
|
||||
{
|
||||
ARPCachePut(packet->source_ip_address, packet->ethernet_frame->source_address);
|
||||
|
||||
ICMPSendReply(packet->source_ip_address,
|
||||
header->identifier,
|
||||
header->sequence_number,
|
||||
header->checksum,
|
||||
packet->data + sizeof(CICMPHeader),
|
||||
packet->length - sizeof(CICMPHeader)); // wut
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
324
src/Home/Net/IPV4.CC
Executable file
324
src/Home/Net/IPV4.CC
Executable file
@ -0,0 +1,324 @@
|
||||
#include "ARP"
|
||||
|
||||
#define IPV4_ERR_ADDR_INVALID -200001
|
||||
#define IPV4_ERR_HOST_UNREACHABLE -200002
|
||||
|
||||
//i'd like to know why 64 was chosen
|
||||
#define IPV4_TTL 64
|
||||
|
||||
//Look up IP Protocol Numbers online to see many more.
|
||||
#define IP_PROTOCOL_ICMP 0x01
|
||||
#define IP_PROTOCOL_TCP 0x06
|
||||
#define IP_PROTOCOL_UDP 0x11
|
||||
|
||||
|
||||
class CIPV4Packet
|
||||
{
|
||||
CEthernetFrame *ethernet_frame;
|
||||
|
||||
U32 source_ip_address;
|
||||
U32 destination_ip_address;
|
||||
|
||||
U8 protocol;
|
||||
|
||||
U8 padding[7];
|
||||
|
||||
U8 *data;
|
||||
|
||||
I64 length;
|
||||
};
|
||||
|
||||
class CIPV4Header
|
||||
{ // note: U4's in some U8s.
|
||||
U8 version_ihl; //Version for IPV4 is 4. IHL=Internet Header Length
|
||||
|
||||
U8 dscp_ecn; // DSCP=Differentiated Services Code Point. ECN=Explicit Congestion Notification
|
||||
|
||||
U16 total_length; // min 20B max 65535
|
||||
|
||||
U16 identification;
|
||||
|
||||
U16 flags_fragment_offset; // flags first(?) 3 bits. fragment offset min 0 max 65528
|
||||
// flag: bit 0: reserved must be 0. bit 1: don't fragment. bit 2: more fragments
|
||||
|
||||
U8 time_to_live; // specified in seconds, wikipedia says nowadays serves as a hop count
|
||||
|
||||
U8 protocol;
|
||||
|
||||
U16 header_checksum;
|
||||
|
||||
U32 source_ip_address;
|
||||
U32 destination_ip_address;
|
||||
}
|
||||
|
||||
class CIPV4Globals
|
||||
{ // _be indicates Big Endian
|
||||
U32 local_ip;
|
||||
U32 local_ip_be;
|
||||
|
||||
U32 ipv4_router_address;
|
||||
U32 ipv4_subnet_mask;
|
||||
|
||||
} ipv4_globals;
|
||||
|
||||
U0 InitIPV4Globals()
|
||||
{
|
||||
ipv4_globals.local_ip = 0;
|
||||
ipv4_globals.local_ip_be = 0;
|
||||
ipv4_globals.ipv4_router_address = 0;
|
||||
ipv4_globals.ipv4_subnet_mask = 0;
|
||||
}
|
||||
|
||||
|
||||
// For now, trusting Shrine's implement
|
||||
// of this. Shrine links back to
|
||||
// http://stackoverflow.com/q/26774761/2524350
|
||||
|
||||
U16 IPV4Checksum(U8* header, I64 length)
|
||||
{ //todo. make names clearer, and better comments.
|
||||
I64 nleft = length;
|
||||
U16 *w = header;
|
||||
I64 sum = 0;
|
||||
|
||||
while (nleft > 1)
|
||||
{
|
||||
sum += *(w++);
|
||||
nleft -= 2;
|
||||
}
|
||||
|
||||
// "mop up an odd byte, if necessary"
|
||||
if (nleft == 1)
|
||||
{
|
||||
sum += ((*w) & 0x00FF);
|
||||
}
|
||||
|
||||
// "add back carry outs from top 16 bits to low 16 bits"
|
||||
sum = (sum >> 16) + (sum & 0xFFFF); // "add hi 16 to low 16"
|
||||
sum += (sum >> 16); // add carry
|
||||
return (~sum) & 0xFFFF;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
I64 GetMACAddressForIP(U32 ip_address, U8 **mac_out)
|
||||
{
|
||||
CARPHash *entry;
|
||||
I64 retries;
|
||||
I64 attempt;
|
||||
/*
|
||||
switch (ip_address)
|
||||
{
|
||||
case 0:
|
||||
return IPV4_ERR_ADDR_INVALID;
|
||||
case 0xFFFFFFFF:
|
||||
*mac_out = ethernet_globals.ethernet_broadcast;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
if (ip_address == 0)
|
||||
{
|
||||
ZenithLog("Get MAC for IP failed. Address = 0\n");
|
||||
return IPV4_ERR_ADDR_INVALID;
|
||||
}
|
||||
if (ip_address == 0xFFFFFFFF)
|
||||
{
|
||||
ZenithLog("Get MAC for IP requested and returning ethernet broadcast\n");
|
||||
*mac_out = ethernet_globals.ethernet_broadcast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// "outside this subnet; needs routing"
|
||||
if ((ip_address & ipv4_globals.ipv4_subnet_mask) !=
|
||||
(ipv4_globals.local_ip & ipv4_globals.ipv4_subnet_mask))
|
||||
{
|
||||
// Shrine recurses here... and says FIXME infinite loop if mis-configured...
|
||||
}
|
||||
else // "local network"
|
||||
{
|
||||
entry = ARPCacheFindByIP(ip_address);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
*mac_out = entry->mac_address;
|
||||
return 0;
|
||||
}
|
||||
//else, not in cache, need to request it
|
||||
|
||||
// "Up to 4 retries, 500 ms each"
|
||||
retries = 4;
|
||||
while (retries)
|
||||
{
|
||||
attempt = 0;
|
||||
for (attempt = 0; attempt < 50; attempt++)
|
||||
{
|
||||
Sleep(10);
|
||||
entry = ARPCacheFindByIP(ip_address);
|
||||
if (entry) break;
|
||||
}
|
||||
|
||||
if (entry)
|
||||
{
|
||||
*mac_out = entry->mac_address;
|
||||
return 0;
|
||||
}
|
||||
|
||||
retries--;
|
||||
}
|
||||
|
||||
//Shrine does some in_addr mess to log error
|
||||
ZenithLog("Failed to resolve address %d",ip_address);
|
||||
return IPV4_ERR_HOST_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
I64 IPV4PacketAllocate(U8 **frame_out,
|
||||
U8 protocol,
|
||||
U32 source_ip_address,
|
||||
U32 destination_ip_address,
|
||||
I64 length)
|
||||
{
|
||||
U8 *ethernet_frame;
|
||||
U8 *destination_mac_address;
|
||||
|
||||
I64 error;
|
||||
I64 de_index;
|
||||
|
||||
I64 internet_header_length;
|
||||
|
||||
CIPV4Header *header;
|
||||
|
||||
|
||||
error = GetMACAddressForIP(destination_ip_address, &destination_mac_address);
|
||||
if (error < 0)
|
||||
{
|
||||
ZenithLog("IPV4 Packet Allocate failed to get MAC for destination.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
de_index = EthernetFrameAllocate(ðernet_frame,
|
||||
EthernetGetMAC(),
|
||||
destination_mac_address,
|
||||
ETHERTYPE_IPV4,
|
||||
sizeof(CIPV4Header) + length);
|
||||
if (de_index < 0)
|
||||
{
|
||||
ZenithLog("IPV4 Ethernet Frame Allocate failed.\n");
|
||||
return de_index;
|
||||
}
|
||||
|
||||
internet_header_length = 5;// ... why. need a #define
|
||||
|
||||
|
||||
header = ethernet_frame;
|
||||
|
||||
|
||||
header->version_ihl = internet_header_length | (4 << 4);// whaaat the fuck is this? #define!
|
||||
|
||||
header->dscp_ecn = 0; // a clear define of what this actually means would be good
|
||||
|
||||
header->total_length = EndianU16(internet_header_length * 4 + length); //...why?
|
||||
|
||||
header->identification = 0;// define would be clearer
|
||||
|
||||
header->flags_fragment_offset = 0; // define would be clearer
|
||||
|
||||
header->time_to_live = IPV4_TTL;
|
||||
|
||||
header->protocol = protocol;
|
||||
|
||||
header->header_checksum = 0; // why is 0 ok?
|
||||
|
||||
header->source_ip_address = EndianU32(source_ip_address);
|
||||
|
||||
header->destination_ip_address = EndianU32(destination_ip_address);
|
||||
|
||||
header->header_checksum = IPV4Checksum(header, internet_header_length + 4);//why the 4's...
|
||||
|
||||
*frame_out = ethernet_frame + sizeof(CIPV4Header);
|
||||
return de_index;
|
||||
}
|
||||
|
||||
U0 IPV4PacketFinish(I64 de_index) //alias for EthernetFrameFinish
|
||||
{
|
||||
EthernetFrameFinish(de_index);
|
||||
}
|
||||
|
||||
U32 IPV4GetAddress()
|
||||
{
|
||||
return ipv4_globals.local_ip;
|
||||
}
|
||||
|
||||
U0 IPV4SetAddress(U32 ip_address)
|
||||
{
|
||||
ipv4_globals.local_ip = ip_address;
|
||||
ipv4_globals.local_ip_be = EndianU32(ip_address);
|
||||
|
||||
ARPSetIPV4Address(ip_address);
|
||||
|
||||
}
|
||||
|
||||
U0 IPV4SetSubnet(U32 router_address, U32 subnet_mask)
|
||||
{
|
||||
ipv4_globals.ipv4_router_address = router_address;
|
||||
ipv4_globals.ipv4_subnet_mask = subnet_mask;
|
||||
}
|
||||
|
||||
//I64
|
||||
U0 IPV4ParsePacket(CIPV4Packet *packet_out, CEthernetFrame *ethernet_frame)
|
||||
{
|
||||
//...if ethertype not ipv4 error?
|
||||
|
||||
//Shrine says FIXME check ethernet_frame length !! ... we need to know what's appropriate
|
||||
|
||||
CIPV4Header *header = ethernet_frame->data;
|
||||
I64 header_length = (header->version_ihl & 0x0F) * 4;//this Has to go. at least abstract or something..
|
||||
U16 total_length = EndianU16(header->total_length);
|
||||
|
||||
packet_out->ethernet_frame = ethernet_frame;
|
||||
|
||||
packet_out->source_ip_address = EndianU32(header->source_ip_address);
|
||||
packet_out->destination_ip_address = EndianU32(header->destination_ip_address);
|
||||
|
||||
packet_out->protocol = header->protocol;
|
||||
|
||||
|
||||
packet_out->data = ethernet_frame->data + header_length;
|
||||
|
||||
packet_out->length = total_length - header_length;
|
||||
|
||||
// return 0;
|
||||
}
|
||||
|
||||
// IPV4 handler moved to NetHandlerTask file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
InitIPV4Globals;
|
@ -1,3 +1,23 @@
|
||||
U0 IPV4Handler(CEthernetFrame *ethernet_frame)
|
||||
{
|
||||
CIPV4Packet packet;
|
||||
|
||||
IPV4ParsePacket(&packet, ethernet_frame);
|
||||
|
||||
ARPCachePut(packet.source_ip_address, ethernet_frame->source_address);
|
||||
|
||||
switch (packet.protocol)
|
||||
{
|
||||
case IP_PROTOCOL_ICMP:
|
||||
ICMPHandler(&packet);
|
||||
break;
|
||||
case IP_PROTOCOL_TCP:
|
||||
break;
|
||||
case IP_PROTOCOL_UDP:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
U0 HandleNetQueueEntry(CNetQueueEntry *entry)
|
||||
{
|
||||
@ -10,6 +30,9 @@ U0 HandleNetQueueEntry(CNetQueueEntry *entry)
|
||||
case ETHERTYPE_ARP:
|
||||
ARPHandler(ðernet_frame);
|
||||
break;
|
||||
case ETHERTYPE_IPV4:
|
||||
IPV4Handler(ðernet_frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +48,8 @@ U0 NetHandlerTask(I64)
|
||||
}
|
||||
else
|
||||
{
|
||||
LBts(&Fs->task_flags, TASKf_SUSPENDED);
|
||||
LBts(&Fs->task_flags, TASKf_IDLE);
|
||||
//ZenithLog("IDLE: NetHandler\n");
|
||||
Yield;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
//Shrine mentions possibly using two FIFOs (in our case, Queues) for pending and empty frames.
|
||||
//If logical to implement, perhaps Zenith NetQueue code should do something similar to that idea.
|
||||
/* Shrine mentions possibly using two FIFOs
|
||||
(in our case, Queues) for pending and
|
||||
empty frames. If logical to implement,
|
||||
perhaps Zenith NetQueue code should
|
||||
do something similar to that idea. */
|
||||
|
||||
//Each Ethernet Frame will be represented as an entry in a CQueue.
|
||||
/* Each Ethernet Frame will be represented
|
||||
as an entry in a CQueue. */
|
||||
class CNetQueueEntry:CQueue
|
||||
{
|
||||
I64 length; //todo: change to packet_length?
|
||||
@ -16,17 +20,21 @@ class CNetQueueEntry:CQueue
|
||||
itself, the Head. */
|
||||
CQueue *net_queue; // no QueueRemove the Head! only Entries!
|
||||
|
||||
//Net Handler Task is set idle and active depending on if entries in Net Queue. See NetHandlerTask.CC
|
||||
/* Net Handler Task is set idle and active depending
|
||||
on if entries in Net Queue. See NetHandlerTask.CC */
|
||||
CTask *net_handler_task = NULL;
|
||||
|
||||
|
||||
U0 NetQueueInit()
|
||||
{
|
||||
net_queue = CAlloc(sizeof(CQueue));
|
||||
QueueInit(net_queue);
|
||||
}
|
||||
|
||||
|
||||
CNetQueueEntry *NetQueuePull()
|
||||
{//Returns a pointer to a CNetQueueEntry, or NULL pointer if Net Queue is empty.
|
||||
{/* Returns a pointer to a CNetQueueEntry,
|
||||
or NULL pointer if Net Queue is empty. */
|
||||
CNetQueueEntry *entry;
|
||||
|
||||
if (net_queue->next != net_queue)
|
||||
@ -35,11 +43,15 @@ CNetQueueEntry *NetQueuePull()
|
||||
QueueRemove(entry);
|
||||
}
|
||||
else // Queue is empty if head->next is head itself.
|
||||
{
|
||||
entry = NULL;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
U0 NetQueuePushCopy(U8 *data, I64 length)
|
||||
{/* Pushes a copy of the packet data and length
|
||||
into the Net Queue. The NetQueueEntry is inserted
|
||||
@ -53,8 +65,9 @@ U0 NetQueuePushCopy(U8 *data, I64 length)
|
||||
QueueInsert(entry, net_queue->last);
|
||||
|
||||
//Set Net Handler Task active.
|
||||
ZenithLog("ACTIVE: NetHandler\n");
|
||||
if (net_handler_task)
|
||||
LBtr(&net_handler_task->task_flags, TASKf_SUSPENDED);
|
||||
LBtr(&net_handler_task->task_flags, TASKf_IDLE);
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
- Clear documentation.
|
||||
*/
|
||||
|
||||
#include "Net/Net.HH"
|
||||
#include "Net/NetQueue"
|
||||
#include "Net.HH"
|
||||
#include "NetQueue"
|
||||
|
||||
#define PCNET_DEVICE_ID 0x2000
|
||||
#define PCNET_VENDOR_ID 0x1022
|
||||
@ -68,8 +68,8 @@
|
||||
#define PCNET_CTRL_STOP 2
|
||||
#define PCNET_CTRL_RINT 10
|
||||
|
||||
#define PCNET_RX_BUFF_COUNT 32 // Linux & Shrine Driver use 32 and 8 for these, we could allow more if wanted.
|
||||
#define PCNET_TX_BUFF_COUNT 8
|
||||
#define PCNET_RX_BUFF_COUNT 32 // Linux & Shrine Driver use 32 and 8 for
|
||||
#define PCNET_TX_BUFF_COUNT 8 // these, we could allow more if wanted.
|
||||
|
||||
#define PCNET_DESCRIPTORf_ENP 24
|
||||
#define PCNET_DESCRIPTORf_STP 25
|
||||
@ -81,7 +81,7 @@
|
||||
class CPCNet
|
||||
{
|
||||
CPCIDev *pci;
|
||||
U64 mac_address; //MAC address is first 6 bytes of PCNet EEPROM (page # ?)
|
||||
U8 mac_address[6]; //MAC address is first 6 bytes of PCNet EEPROM (page # ? )
|
||||
|
||||
I64 current_rx_de_index; // Current Receive DE being processed. Gets incremented, wrapped to 0 at max of PCNET_RX_BUFF_COUNT.
|
||||
I64 current_tx_de_index; // Current Transmit DE being processed. Gets incremented, wrapped to 0 at max of PCNET_TX_BUFF_COUNT.
|
||||
@ -97,29 +97,36 @@ class CPCNet
|
||||
} pcnet; // pcnet is the global variable we store all of this into.
|
||||
|
||||
class CPCNetDescriptorEntry
|
||||
{/* AMD PCNet datasheet p.1-991 & p.1-994 NOTE: chart typo on 1-994, see ONES and BCNT on 1-995.
|
||||
TX and RX DE's are the same size (16-Bytes) and structure, but have different registers and functions.
|
||||
The RX and TX DE buffers of the CPCNet class are allocated to a certain amount of these DEs. */
|
||||
{/* AMD PCNet datasheet p.1-991 & p.1-994 NOTE: chart typo on 1-994, see ONES and BCNT on 1-995.
|
||||
TX and RX DE's are the same size (16-Bytes) and structure,
|
||||
but have different registers and functions.
|
||||
The RX and TX DE buffers of the CPCNet class
|
||||
are allocated to a certain amount of these DEs. */
|
||||
U32 buffer_addr;
|
||||
U32 status1;
|
||||
U32 status2;
|
||||
U32 reserved;
|
||||
};
|
||||
|
||||
|
||||
CPCIDev *PCNetPCIDevFind()
|
||||
{// Find and return PCNetII card as a CPCIDev pointer.
|
||||
return PCIDevFind(,,PCNET_VENDOR_ID,PCNET_DEVICE_ID);
|
||||
}
|
||||
|
||||
U32 PCNetGetIOBase()
|
||||
{/* Return memory IO base address of PCNet card. Bits 0-4 are not for the IO base, so an AND NOT 0x1F ignores those bits. */
|
||||
{/* Return memory IO base address
|
||||
of PCNet card. Bits 0-4 are not
|
||||
for the IO base, so an AND with
|
||||
~0x1F ignores those bits. */
|
||||
U32 io_base = pcnet.pci->base[0] & ~0x1F;
|
||||
return io_base;
|
||||
}
|
||||
|
||||
U0 PCNetReset()
|
||||
{/* Reads the 32- and 16-bit RESET registers, which,
|
||||
regardless of which mode the card is in, will reset it back to 16-bit mode. */
|
||||
{/* Reads the 32- and 16-bit RESET registers,
|
||||
which, regardless of which mode the card is in,
|
||||
will reset it back to 16-bit mode. */
|
||||
InU32(PCNetGetIOBase + PCNET_DW_RESET);
|
||||
InU16(PCNetGetIOBase + PCNET_WD_RESET);
|
||||
Busy(5); // OSDev says minimum 1 æS
|
||||
@ -127,31 +134,49 @@ U0 PCNetReset()
|
||||
|
||||
U0 PCNetEnter32BitMode()
|
||||
{/* AMD PCNet datasheet p. 1-930
|
||||
Summary: A 32-bit write (while in 16-bit mode) to RDP will cause 16-bit mode exit and immediate entry into 32-bit mode. */
|
||||
Summary: A 32-bit write (while in 16-bit mode)
|
||||
to RDP will cause 16-bit mode exit
|
||||
and immediate enter into 32-bit mode. */
|
||||
OutU32(PCNetGetIOBase + PCNET_DW_RDP, 0);
|
||||
|
||||
}
|
||||
|
||||
U0 PCNetWriteRAP(U32 value)
|
||||
{/* AMD PCNet datasheet p. 1-952
|
||||
Summary: Register Address Pointer register
|
||||
value will indicate which CSR / BCR register
|
||||
we want to access in RDP / BDP. */
|
||||
OutU32(PCNetGetIOBase + PCNET_DW_RAP, value);
|
||||
}
|
||||
|
||||
U0 PCNetWriteCSR(U32 csr, U32 value)
|
||||
{/* AMD PCNet datasheet p. 1-952
|
||||
Summary: Control and Status Registers are accessed via the RDP (Register Data Port).
|
||||
Which CSR is selected is based on the value in the RAP (Register Address Port). */
|
||||
OutU32(PCNetGetIOBase + PCNET_DW_RAP, csr);
|
||||
Summary: Control and Status Registers are
|
||||
accessed via the RDP (Register Data Port).
|
||||
Which CSR is selected is based on the value
|
||||
in the RAP. */
|
||||
PCNetWriteRAP(csr);
|
||||
OutU32(PCNetGetIOBase + PCNET_DW_RDP, value);
|
||||
}
|
||||
|
||||
U32 PCNetReadCSR(U32 csr)
|
||||
{/* AMD PCNet datasheet p. 1-952
|
||||
Summary: Control and Status Registers are accessed via the RDP (Register Data Port).
|
||||
Which CSR is selected is based on the value in the RAP. */
|
||||
OutU32(PCNetGetIOBase + PCNET_DW_RAP, csr);
|
||||
Summary: Control and Status Registers are
|
||||
accessed via the RDP (Register Data Port).
|
||||
Which CSR is selected is based on the value
|
||||
in the RAP. */
|
||||
PCNetWriteRAP(csr);
|
||||
return InU32(PCNetGetIOBase + PCNET_DW_RDP);
|
||||
}
|
||||
|
||||
U0 PCNetSetSWStyle()
|
||||
{/* AMD PCNet datasheet p. 1-968
|
||||
In CSR58 (Software Style), the 8-bit SWSTYLE register dictates interpretation of certain bits in the CSR space,
|
||||
and widths of descriptors and initialization block.
|
||||
In PCNet-PCI mode, CSR4 bits function as defined in the datasheet, and TMD1[29] functions as ADD_FCS. */
|
||||
In CSR58 (Software Style), the 8-bit
|
||||
SWSTYLE register dictates interpretation of certain
|
||||
bits in the CSR space, and widths of descriptors and
|
||||
initialization block. In PCINet-PCI mode, CSR4 bits
|
||||
function as defined in the datasheet , and TMD1[29]
|
||||
functions as ADD_FCS. */
|
||||
U32 csr = PCNetReadCSR(PCNET_CSR_SOFTWARESTYLE);
|
||||
|
||||
csr &= ~0xFF; // clears first 8 bits: SWSTYLE 8-bit register.
|
||||
@ -163,23 +188,35 @@ U0 PCNetSetSWStyle()
|
||||
U0 PCNetGetMAC()
|
||||
{/* AMD PCNet datasheet p. 1-887, 1-931, 1-937
|
||||
MAC address stored at first 6 bytes of PCNet EEPROM.
|
||||
EEPROM addresses shadow-copied to APROM at hardware init. APROM accessible at first 16 bytes of PCI IO space. */
|
||||
EEPROM addresses shadow-copied to APROM at hardware init.
|
||||
APROM accessible at first 16 bytes of PCI IO space. */
|
||||
I64 i;
|
||||
for (i = 0; i < 6; i++)
|
||||
pcnet.mac_address.u8[i] = InU8(PCNetGetIOBase + i);
|
||||
{
|
||||
pcnet.mac_address[i] = InU8(PCNetGetIOBase + i);
|
||||
}
|
||||
}
|
||||
|
||||
U0 PCNetInitDescriptorEntry(CPCNetDescriptorEntry *entry, U32 buffer_addr, I64 is_rx)
|
||||
U0 PCNetInitDescriptorEntry(CPCNetDescriptorEntry *entry, U32 buffer_address, I64 is_rx)
|
||||
{
|
||||
entry->buffer_addr = buffer_addr;
|
||||
entry->buffer_addr = buffer_address;
|
||||
|
||||
/* AMD PCNet datasheet p.1-991.
|
||||
BCNT is the usable buffer length, expressed as first 12 bits of 2s-complement of desired length.
|
||||
Bits 0-11 of a DE are for the buffer byte count (BCNT),and bits 12-15 of a DE must be written all ones (ONES)*/
|
||||
entry->status1 |= -ETHERNET_MIN_FRAME_SIZE & 0xFFF; //Sets BCNT reg (first 12 bits) in DE TMD1/RMD1, as 2s complement of the desired length.
|
||||
BCNT is the usable buffer length, expressed as first
|
||||
12 bits of 2s-complement of desired length.
|
||||
Bits 0-11 of a DE are for the buffer byte count (BCNT),
|
||||
and bits 12-15 of a DE must be written all ones (ONES) */
|
||||
U16 buffer_byte_count = -ETHERNET_FRAME_SIZE; // Sets up as 2s complement of the desired length.
|
||||
buffer_byte_count &= 0x0FFF; // Masks 0 over everything except bits 0-11.
|
||||
|
||||
entry->status1 |= buffer_byte_count; // Sets BCNT reg (first 12 bits) in DE TMD1/RMD1.
|
||||
entry->status1 |= 0xF000; // Sets bits 12-15 (ONES) in DE TMD1/RMD1 as all ones.
|
||||
|
||||
//if this is a Receive DE, give ownership to the card so the PCNet can fill them.
|
||||
if (is_rx) Bts(&entry->status1, PCNET_DESCRIPTORf_OWN);
|
||||
if (is_rx)
|
||||
Bts(&entry->status1, PCNET_DESCRIPTORf_OWN);
|
||||
ClassRep(entry);
|
||||
|
||||
}
|
||||
|
||||
U0 PCNetAllocateBuffers()
|
||||
@ -187,82 +224,131 @@ U0 PCNetAllocateBuffers()
|
||||
I64 de_index; // used in for loops for TX and RX DE access.
|
||||
|
||||
/* AMD PCNet datasheet p.1-913, p.1-990
|
||||
When SSIZE32 = 1, Descriptor Ring Entry Base Address must be on 16-byte boundary. (TDRA[3:0] = 0, RDRA[3:0] = 0) */
|
||||
pcnet.rx_de_buffer_phys = CAllocAligned(sizeof(CPCNetDescriptorEntry) * PCNET_RX_BUFF_COUNT, 16, Fs->code_heap);
|
||||
pcnet.tx_de_buffer_phys = CAllocAligned(sizeof(CPCNetDescriptorEntry) * PCNET_TX_BUFF_COUNT, 16, Fs->code_heap);
|
||||
When SSIZE32=1, Descriptor Ring Entry Base Address
|
||||
must be on 16-byte boundary. (TDRA[3:0]=0, RDRA[3:0]=0) */
|
||||
pcnet.rx_de_buffer_phys = CAllocAligned(sizeof(CPCNetDescriptorEntry) * PCNET_RX_BUFF_COUNT,
|
||||
16,
|
||||
Fs->code_heap);
|
||||
pcnet.tx_de_buffer_phys = CAllocAligned(sizeof(CPCNetDescriptorEntry) * PCNET_TX_BUFF_COUNT,
|
||||
16,
|
||||
Fs->code_heap);
|
||||
|
||||
//Shrine does a check and returns -1 here, if the end of either buffer exceeds 0x100000000
|
||||
|
||||
pcnet.rx_de_buffer = dev.uncached_alias + pcnet.rx_de_buffer_phys; // we want uncached
|
||||
pcnet.tx_de_buffer = dev.uncached_alias + pcnet.tx_de_buffer_phys; // access to these.
|
||||
|
||||
pcnet.rx_buffer_addr = CAlloc(ETHERNET_MIN_FRAME_SIZE * PCNET_RX_BUFF_COUNT, Fs->code_heap);//Shrine has a TODO to figure out
|
||||
pcnet.tx_buffer_addr = CAlloc(ETHERNET_MIN_FRAME_SIZE * PCNET_TX_BUFF_COUNT, Fs->code_heap);//if these should be uncached too.
|
||||
//note, p.1-991,1-994: RBADR is only 32 bits wide.
|
||||
pcnet.rx_buffer_addr = CAlloc(ETHERNET_FRAME_SIZE * PCNET_RX_BUFF_COUNT, //Shrine has a TODO to figure out
|
||||
Fs->code_heap);
|
||||
pcnet.tx_buffer_addr = CAlloc(ETHERNET_FRAME_SIZE * PCNET_TX_BUFF_COUNT, //if these should be uncached too.
|
||||
Fs->code_heap); //note, p.1-991,1-994: RBADR is only 32 bits wide.
|
||||
|
||||
//Shrine does a check and returns -1 here, if the end of either buffer exceeds 0x100000000
|
||||
|
||||
|
||||
CPCNetDescriptorEntry *entry = pcnet.rx_de_buffer;
|
||||
for (de_index = 0; de_index < PCNET_RX_BUFF_COUNT; de_index++)
|
||||
{
|
||||
PCNetInitDescriptorEntry(&entry[de_index], pcnet.rx_buffer_addr, TRUE); // TRUE for is_rx.
|
||||
}
|
||||
|
||||
entry = pcnet.tx_de_buffer;
|
||||
for (de_index = 0; de_index < PCNET_TX_BUFF_COUNT; de_index++)
|
||||
PCNetInitDescriptorEntry(&entry[de_index], pcnet.tx_buffer_addr, FALSE); // FALSE for is_rx.
|
||||
{
|
||||
PCNetInitDescriptorEntry(&entry[de_index], pcnet.tx_buffer_addr, FALSE); // FALSE for is_rx.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
U0 PCNetDirectInit()
|
||||
{/* AMD PCNet datasheet p. 1-1021
|
||||
Instead of setting up an initialization block,
|
||||
direct writes to the necessary CSRs can be used to manually initialize the PCNet card. */
|
||||
Instead of setting up initialization block,
|
||||
direct writes to the necessary CSRs can be
|
||||
used to manually initialize the PCNet card. */
|
||||
|
||||
/* AMD PCNet datasheet p.1-991
|
||||
If Logical Address Filter is set as all 0, all incoming logical addresses are rejected. Disables multicast. */
|
||||
If Logical Address Filter is set as
|
||||
all 0, all incoming logical addresses
|
||||
are rejected. Disables multicast. */
|
||||
PCNetWriteCSR(PCNET_CSR_LADRF0, 0);
|
||||
PCNetWriteCSR(PCNET_CSR_LADRF1, 0);
|
||||
PCNetWriteCSR(PCNET_CSR_LADRF2, 0);
|
||||
PCNetWriteCSR(PCNET_CSR_LADRF3, 0);
|
||||
|
||||
/* AMD PCNet datasheet p.1-960, 1-961
|
||||
The Physical Address is the MAC.
|
||||
The first 16 bits of CSRs 12-14 are for the Physical Address, the upper bits are reserved, written 0 read undefined.*/
|
||||
/* The Physical Address is the MAC.
|
||||
AMD PCNet datasheet p.1-960, 1-961
|
||||
The first 16 bits of CSRs 12-14 are
|
||||
for the Physical Address, the upper bits
|
||||
are reserved, written 0 read undefined.
|
||||
|
||||
PCNetWriteCSR(PCNET_CSR_PADR0, pcnet.mac_address.u16[0]);
|
||||
PCNetWriteCSR(PCNET_CSR_PADR1, pcnet.mac_address.u16[1]);
|
||||
PCNetWriteCSR(PCNET_CSR_PADR2, pcnet.mac_address.u16[2]);
|
||||
The OR and bit-shift of 8 allows writing
|
||||
separate U8 values in the correct locations
|
||||
of the CSR. */
|
||||
PCNetWriteCSR(PCNET_CSR_PADR0,
|
||||
pcnet.mac_address[0] | (pcnet.mac_address[1] << 8));
|
||||
PCNetWriteCSR(PCNET_CSR_PADR1,
|
||||
pcnet.mac_address[2] | (pcnet.mac_address[3] << 8));
|
||||
PCNetWriteCSR(PCNET_CSR_PADR2,
|
||||
pcnet.mac_address[4] | (pcnet.mac_address[5] << 8));
|
||||
|
||||
/* AMD PCNet datasheet p.1-961, 1-962, 1-963
|
||||
Refer to datasheet for specifics. Most relevant, when setting Mode to 0, promiscuous mode is is disabled,
|
||||
TX and RX enabled, enable RX broadcast and unicast. */
|
||||
Refer to datasheet for specifics.
|
||||
Most relevant, when setting Mode to 0,
|
||||
promiscuous mode is is disabled, TX and
|
||||
RX enabled, enable RX broadcast and unicast. */
|
||||
PCNetWriteCSR(PCNET_CSR_MODE, 0);
|
||||
|
||||
/* AMD PCNet datasheet p.1-964
|
||||
CSR 24 and 25 need to be filled with the lower and upper 16 bits, respectively, of the address of the RX packet ring. Likewise for
|
||||
CSR 30 and 31 for the TX packet ring.*/
|
||||
CSR 24 and 25 need to be filled
|
||||
with the lower and upper 16 bits,
|
||||
respectively, of the address of
|
||||
the RX packet ring. Likewise for
|
||||
CSR 30 and 31 for the TX packet ring.
|
||||
|
||||
PCNetWriteCSR(PCNET_CSR_BADRL, pcnet.rx_buffer_addr.u16[0]);
|
||||
PCNetWriteCSR(PCNET_CSR_BADRU, pcnet.rx_buffer_addr.u16[1]);
|
||||
0xFFFF AND on address will leave
|
||||
only lower 16 bits remaining.
|
||||
|
||||
PCNetWriteCSR(PCNET_CSR_BADTL, pcnet.tx_buffer_addr.u16[0]);
|
||||
PCNetWriteCSR(PCNET_CSR_BADTU, pcnet.tx_buffer_addr.u16[1]);
|
||||
Bitshift right of 16 will replace
|
||||
first 16 bits with upper 16 bits,
|
||||
remaining bits cleared.*/
|
||||
PCNetWriteCSR(PCNET_CSR_BADRL,
|
||||
pcnet.rx_buffer_addr & 0xFFFF);
|
||||
PCNetWriteCSR(PCNET_CSR_BADRU,
|
||||
pcnet.rx_buffer_addr >> 16);
|
||||
|
||||
PCNetWriteCSR(PCNET_CSR_BADTL,
|
||||
pcnet.tx_buffer_addr & 0xFFFF);
|
||||
PCNetWriteCSR(PCNET_CSR_BADTU,
|
||||
pcnet.tx_buffer_addr >> 16);
|
||||
|
||||
/* AMD PCNet datasheet p. 1-967
|
||||
Default value at hardware init is all 0.
|
||||
Standard init block process sets this, but if doing directly it is imperative to manually set it 0. */
|
||||
Default value at hardware init is
|
||||
all 0. Standard init block process
|
||||
sets this, but if doing directly
|
||||
it is imperative to manually set it 0. */
|
||||
PCNetWriteCSR(PCNET_CSR_POLLINT, 0);
|
||||
|
||||
/* AMD PCNet datasheet p. 1-970
|
||||
Receive and Transmit Ring Length CSRs bits 0-15 need to be set as the 2s complement of the ring length.
|
||||
The AND with 0xFFFF clears the upper Reserved bits, which are to be written as zeroes read undefined. */
|
||||
PCNetWriteCSR(PCNET_CSR_RXRINGLEN, -PCNET_RX_BUFF_COUNT & 0xFFFF);
|
||||
PCNetWriteCSR(PCNET_CSR_TXRINGLEN, -PCNET_TX_BUFF_COUNT & 0xFFFF);
|
||||
Receive and Transmit Ring Length CSRs
|
||||
bits 0-15 need to be set as the 2s complement
|
||||
of the ring length. The AND with 0xFFFF clears
|
||||
the upper Reserved bits, which are to be written
|
||||
as zeroes read undefined. */
|
||||
PCNetWriteCSR(PCNET_CSR_RXRINGLEN,
|
||||
-PCNET_RX_BUFF_COUNT & 0xFFFF);
|
||||
PCNetWriteCSR(PCNET_CSR_TXRINGLEN,
|
||||
-PCNET_TX_BUFF_COUNT & 0xFFFF);
|
||||
|
||||
|
||||
}
|
||||
|
||||
U0 PCNetSetInterruptCSR()
|
||||
{/* AMD PCNet datasheet p.1-952, 1-953, 1-954, 1-955, 1-956, 1-957
|
||||
Refer to datasheet for specifics on the Interrupt Masks.
|
||||
Most of these, when set 0, allow interrupts to be set in CSR0.
|
||||
We set Big-Endian disabled, RX interrupts enabled, Init Done interrupt disabled, and TX interrupt disabled. */
|
||||
We set Big-Endian disabled, RX interrupts
|
||||
enabled, Init Done interrupt disabled, and TX interrupt
|
||||
disabled. */
|
||||
U32 csr = PCNetReadCSR(PCNET_CSR_INTERRUPTS);
|
||||
|
||||
Btr(&csr, PCNET_INT_BSWP);
|
||||
@ -276,7 +362,9 @@ U0 PCNetSetInterruptCSR()
|
||||
|
||||
U0 PCNetEnableTXAutoPad()
|
||||
{/* AMD PCNet datasheet p.1-958
|
||||
Setting bit 11 (Auto Pad Transmit) allows short transmit frames to be automatically extended to 64 bytes. */
|
||||
Setting bit 11 (Auto Pad Transmit) allows
|
||||
shoft transmit frames to be automatically
|
||||
extended to 64 bytes. */
|
||||
U32 csr = PCNetReadCSR(PCNET_CSR_FEATURECTRL);
|
||||
|
||||
Bts(&csr, PCNET_FEATURE_APADXMT);
|
||||
@ -287,7 +375,11 @@ U0 PCNetEnableTXAutoPad()
|
||||
|
||||
U0 PCNetExitConfigMode()
|
||||
{/* AMD PCNet datasheet p.1-954
|
||||
PCNet controller can be started after configuring by ensuring INIT and STOP are cleared and START bit is set in CSR0. */
|
||||
PCNet controller can be started
|
||||
after configuring by ensuring INIT
|
||||
and STOP are cleared and START bit
|
||||
is set, in Status and Control Register
|
||||
(CSR0). */
|
||||
U32 csr = PCNetReadCSR(PCNET_CSR_CTRLSTATUS);
|
||||
|
||||
Btr(&csr, PCNET_CTRL_INIT);
|
||||
@ -295,22 +387,25 @@ U0 PCNetExitConfigMode()
|
||||
|
||||
Bts(&csr, PCNET_CTRL_STRT);
|
||||
|
||||
PCNetWriteCSR(PCNET_CSR_CTRLSTATUS, csr);
|
||||
}
|
||||
|
||||
I64 PCNetDriverOwns(CPCNetDescriptorEntry* entry)
|
||||
{/* Returns whether the value of the OWN bit of the Descriptor Entry is zero.
|
||||
If 0, driver owns, if 1, PCNet card owns it. */
|
||||
{/* Returns whether the value of the OWN bit of the
|
||||
Descriptor Entry is zero. If 0, driver owns,
|
||||
if 1, PCNet card owns it. */
|
||||
return !Bt(&entry->status1, PCNET_DESCRIPTORf_OWN);
|
||||
|
||||
}
|
||||
|
||||
I64 PCNetAllocateTransmitPacket(U8 **packet_buffer_out, I64 length)
|
||||
{/* Transmits the packet at the current TX DE index.
|
||||
The packet_buffer_out is a pointer, since we modify its value, ending with returning the index of the DE we just processed.
|
||||
Length is validated to fit in BCNT.
|
||||
The increment of the current TX DE index is done by assigning it the value of incrementing it AND the max DE index - 1.
|
||||
This will increment it as well as wrap back to 0 if we hit the max DE index. */
|
||||
{/* Transmits the packet at the current TX DE index. The packet_buffer_out
|
||||
is a pointer, since we modify its value, ending with returning the
|
||||
index of the DE we just processed. Length is validated to fit in BCNT.
|
||||
The increment of the current TX DE index is done by assigning it the
|
||||
value of incrementing it AND the max DE index-1. This will increment it
|
||||
as well as wrap back to 0 if we hit the max DE index. */
|
||||
|
||||
U16 buffer_byte_count;
|
||||
I64 de_index = pcnet.current_tx_de_index;
|
||||
|
||||
if (length > 0xFFF)
|
||||
@ -319,6 +414,7 @@ I64 PCNetAllocateTransmitPacket(U8 **packet_buffer_out, I64 length)
|
||||
throw('PCNet');
|
||||
}
|
||||
|
||||
|
||||
CPCNetDescriptorEntry *entry = &pcnet.tx_de_buffer[de_index];
|
||||
|
||||
if (!PCNetDriverOwns(entry))
|
||||
@ -328,27 +424,36 @@ I64 PCNetAllocateTransmitPacket(U8 **packet_buffer_out, I64 length)
|
||||
}
|
||||
|
||||
Bts(&entry->status1, PCNET_DESCRIPTORf_STP);
|
||||
|
||||
Bts(&entry->status1, PCNET_DESCRIPTORf_ENP);
|
||||
|
||||
/* AMD PCNet datasheet p.1-991.
|
||||
BCNT is the usable buffer length, expressed as first 12 bits of 2s-complement of desired length.
|
||||
BCNT is the usable buffer length, expressed as first
|
||||
12 bits of 2s-complement of desired length.
|
||||
Bits 0-11 of a DE are for the buffer byte count (BCNT),
|
||||
and bits 12-15 of a DE must be written all ones (ONES) */
|
||||
entry->status1 |= -length & 0xFFF; // Sets BCNT reg (first 12 bits) in DE TMD1, as 2s complement of the desired length.
|
||||
entry->status1 |= 0xF000; // Sets bits 12-15 (ONES) in DE TMD1 as all ones.
|
||||
buffer_byte_count = -length; // Sets up as 2s complement of the desired length.
|
||||
buffer_byte_count &= 0x0FFF; // Masks 0 over everything except bits 0-11.
|
||||
|
||||
pcnet.current_tx_de_index = (pcnet.current_tx_de_index + 1) & (PCNET_TX_BUFF_COUNT - 1);
|
||||
entry->status1 |= buffer_byte_count; // Sets BCNT reg (first 12 bits) in DE TMD1.
|
||||
entry->status1 |= 0xF000; // Sets bits 12-15 (ONES) in DE TMD1 as all ones.
|
||||
|
||||
*packet_buffer_out = pcnet.tx_buffer_addr + (de_index * ETHERNET_MIN_FRAME_SIZE);
|
||||
pcnet.current_tx_de_index = (pcnet.current_tx_de_index + 1)
|
||||
& (PCNET_TX_BUFF_COUNT - 1);
|
||||
|
||||
*packet_buffer_out = pcnet.tx_buffer_addr + (de_index * ETHERNET_FRAME_SIZE);
|
||||
|
||||
return de_index;
|
||||
|
||||
}
|
||||
|
||||
U0 PCNetFinishTransmitPacket(I64 de_index)
|
||||
{/* Release ownership of the packet to the PCNet card
|
||||
by setting the OWN bit to 1. */
|
||||
CPCNetDescriptorEntry *entry = &pcnet.tx_de_buffer[de_index];
|
||||
|
||||
Bts(&entry->status1, PCNET_DESCRIPTORf_OWN);
|
||||
|
||||
}
|
||||
|
||||
U0 EthernetFrameFinish(I64 de_index)
|
||||
@ -357,38 +462,48 @@ U0 EthernetFrameFinish(I64 de_index)
|
||||
}
|
||||
|
||||
I64 PCNetReceivePacket(U8 **packet_buffer_out, U16 *packet_length_out)
|
||||
{/* Receives the packet at the current RX DE index.
|
||||
Parameters are both pointers, since we modify the value at the packet_buffer_out,
|
||||
and at the packet_length, ending with returning the index of the DE we just processed.
|
||||
The MCNT is stored at the first two bytes of the RMD2.
|
||||
We AND with 0xFFFF to only take in those first two bytes: that is the packet_length.
|
||||
The increment of the current RX DE index is done by assigning it the value of incrementing it AND the max DE index - 1.
|
||||
This will increment it as well as wrap back to 0 if we hit the max DE index. */
|
||||
ZenithLog("PCNet received packet. %X , %X",packet_buffer_out, packet_length_out);
|
||||
{/* Receives the packet at the current RX DE index. Parameters
|
||||
are both pointers, since we modify the value at the packet_buffer_out,
|
||||
and at the packet_length, ending with returning the index of the DE
|
||||
we just processed.
|
||||
The MCNT is stored at the first two bytes of the RMD2. We AND with
|
||||
0xFFFF to only take in those first two bytes: that is the packet_length.
|
||||
The increment of the current RX DE index is done by assigning it the
|
||||
value of incrementing it AND the max DE index-1. This will increment it
|
||||
as well as wrap back to 0 if we hit the max DE index. */
|
||||
ZenithErr("PCNet received packet. %X , %X",packet_buffer_out,packet_length_out);
|
||||
I64 de_index = pcnet.current_rx_de_index;
|
||||
|
||||
CPCNetDescriptorEntry *entry = &pcnet.rx_de_buffer[de_index];
|
||||
|
||||
U16 packet_length = entry->status2 & 0xFFFF;
|
||||
|
||||
pcnet.current_rx_de_index = (pcnet.current_rx_de_index + 1) & (PCNET_RX_BUFF_COUNT - 1);
|
||||
pcnet.current_rx_de_index = (pcnet.current_rx_de_index + 1)
|
||||
& (PCNET_RX_BUFF_COUNT - 1);
|
||||
|
||||
*packet_buffer_out = pcnet.rx_buffer_addr + (de_index * ETHERNET_MIN_FRAME_SIZE);
|
||||
|
||||
*packet_buffer_out = pcnet.rx_buffer_addr + (de_index * ETHERNET_FRAME_SIZE);
|
||||
*packet_length_out = packet_length;
|
||||
return de_index;
|
||||
|
||||
|
||||
}
|
||||
|
||||
U0 PCNetReleaseReceivePacket(I64 de_index)
|
||||
{/* Release ownership of the packet to the PCNet card
|
||||
by setting the OWN bit to 1. */
|
||||
CPCNetDescriptorEntry *entry = &pcnet.rx_de_buffer[de_index];
|
||||
|
||||
Bts(&entry->status1, PCNET_DESCRIPTORf_OWN);
|
||||
}
|
||||
|
||||
interrupt U0 PCNetIRQ()
|
||||
{// todo: comments explaining process...maybe reimplement interrupt handling altogether.
|
||||
|
||||
U8 *packet_buffer;
|
||||
U16 packet_length;
|
||||
I64 de_index;
|
||||
|
||||
U32 csr = PCNetReadCSR(PCNET_CSR_CTRLSTATUS);
|
||||
//"Interrupt Reason: %X , %b\n",csr,csr;
|
||||
|
||||
@ -396,12 +511,12 @@ interrupt U0 PCNetIRQ()
|
||||
|
||||
while (PCNetDriverOwns(&entry[pcnet.current_rx_de_index]))
|
||||
{
|
||||
"%X", pcnet.current_rx_de_index;
|
||||
"%X",pcnet.current_rx_de_index;
|
||||
de_index = PCNetReceivePacket(&packet_buffer, &packet_length);
|
||||
|
||||
if (de_index >= 0) // necessary? check increment logic in PCNetReceivePacket.
|
||||
{
|
||||
ZenithLog("Pushing copy into Net Queue, Releasing Receive Packet");
|
||||
ZenithErr("Pushing copy into Net Queue, Releasing Receive Packet");
|
||||
NetQueuePushCopy(packet_buffer, packet_length);
|
||||
PCNetReleaseReceivePacket(de_index);
|
||||
}
|
||||
@ -415,26 +530,26 @@ interrupt U0 PCNetIRQ()
|
||||
}
|
||||
|
||||
U0 PCIRerouteInterrupts(I64 base)
|
||||
{// todo: comments explaining process, maybe better var names
|
||||
{ // todo: comments explaining process, maybe better var names
|
||||
I64 i;
|
||||
U8 *da = dev.uncached_alias + IOAPIC_REG;
|
||||
U32 *_d = dev.uncached_alias + IOAPIC_DATA;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
*da = IOREDTAB + i * 2 + 1;
|
||||
*da = IOREDTAB + i*2 + 1;
|
||||
*_d = dev.mp_apic_ids[INT_DEST_CPU] << 24;
|
||||
*da = IOREDTAB + i * 2;
|
||||
*da = IOREDTAB + i*2;
|
||||
*_d = 0x4000 + base + i;
|
||||
}
|
||||
}
|
||||
|
||||
U0 PCNetSetupInterrupts()
|
||||
{ // todo: comments explaining process
|
||||
IntEntrySet(I_USER + 0, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER + 1, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER + 2, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER + 3, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER+0, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER+1, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER+2, &PCNetIRQ, IDTET_IRQ);
|
||||
IntEntrySet(I_USER+3, &PCNetIRQ, IDTET_IRQ);
|
||||
PCIRerouteInterrupts(I_USER);
|
||||
}
|
||||
|
||||
@ -446,33 +561,58 @@ U0 PCNetInit()
|
||||
if (!pcnet.pci)
|
||||
return; // if we don't find the card, quit.
|
||||
|
||||
//Clear command register of PCNet PCI device, set IO Enable and Bus Master Enable bits of the register.
|
||||
PCIWriteU16(pcnet.pci->bus, pcnet.pci->dev, pcnet.pci->fun, PCI_REG_COMMAND, PCNET_CMDF_IOEN | PCNET_CMDF_BMEN);
|
||||
/* Clear command register of PCNet
|
||||
PCI device, set IO Enable and Bus
|
||||
Master Enable bits of the register. */
|
||||
PCIWriteU16(pcnet.pci->bus,
|
||||
pcnet.pci->dev,
|
||||
pcnet.pci->fun,
|
||||
PCI_REG_COMMAND,
|
||||
PCNET_CMDF_IOEN | PCNET_CMDF_BMEN);
|
||||
|
||||
PCNetReset;
|
||||
|
||||
PCNetEnter32BitMode;
|
||||
|
||||
PCNetSetSWStyle;
|
||||
|
||||
PCNetGetMAC;
|
||||
// OSDev has code ensuring auto selected connection...
|
||||
|
||||
PCNetAllocateBuffers;
|
||||
|
||||
PCNetDirectInit;
|
||||
|
||||
PCNetSetInterruptCSR;
|
||||
|
||||
PCNetEnableTXAutoPad;
|
||||
|
||||
PCNetExitConfigMode;
|
||||
|
||||
PCNetSetupInterrupts;
|
||||
|
||||
|
||||
Sleep(100);//? necessary?
|
||||
ClassRep(&pcnet);
|
||||
|
||||
"pcnet->rx_de_buffer: %X\n",pcnet.rx_de_buffer;
|
||||
"pcnet->tx_de_buffer: %X\n",pcnet.tx_de_buffer;
|
||||
"pcnet->rx_de_buffer_phys: %X\n",pcnet.rx_de_buffer_phys;
|
||||
"pcnet->rx_de_buffer_phys: %X\n",pcnet.tx_de_buffer_phys;
|
||||
|
||||
|
||||
}
|
||||
|
||||
I64 EthernetFrameAllocate(U8 **packet_buffer_out, U8 *source_address, U8 *destination_address, U16 ethertype, I64 packet_length)
|
||||
{/* Allocate an Ethernet Frame for transmit.
|
||||
The source and destination addresses are copied to the Frame, as well as the ethertype.
|
||||
The packet_buffer_out parameter has the value at its pointer set to the payload of the Ethernet Frame. */
|
||||
I64 EthernetFrameAllocate( U8 **packet_buffer_out,
|
||||
U8 *source_address,
|
||||
U8 *destination_address,
|
||||
U16 ethertype,
|
||||
I64 packet_length)
|
||||
{/* Allocate an Ethernet Frame for transmit. The source
|
||||
and destination addresses are copied to the Frame,
|
||||
as well as the ethertype. The packet_buffer_out
|
||||
parameter has the value at its pointer set to the
|
||||
payload of the Ethernet Frame. */
|
||||
|
||||
//todo: un magic number the rest of this
|
||||
U8 *ethernet_frame;
|
||||
@ -482,7 +622,7 @@ I64 EthernetFrameAllocate(U8 **packet_buffer_out, U8 *source_address, U8 *destin
|
||||
if (packet_length < ETHERNET_MIN_FRAME_SIZE)
|
||||
packet_length = ETHERNET_MIN_FRAME_SIZE;
|
||||
|
||||
de_index = PCNetAllocateTransmitPacket(ðernet_frame, 14 + packet_length);
|
||||
de_index = PCNetAllocateTransmitPacket(ðernet_frame, ETHERNET_MAC_HEADER_LENGTH + packet_length);
|
||||
|
||||
if (de_index < 0)
|
||||
{
|
||||
@ -493,16 +633,35 @@ I64 EthernetFrameAllocate(U8 **packet_buffer_out, U8 *source_address, U8 *destin
|
||||
MemCopy(ethernet_frame, destination_address, MAC_ADDRESS_LENGTH);
|
||||
MemCopy(ethernet_frame + MAC_ADDRESS_LENGTH, source_address, MAC_ADDRESS_LENGTH);
|
||||
|
||||
*ðernet_frame[ETHERNET_ETHERTYPE_OFFSET](U16 *) = ethertype;
|
||||
ethernet_frame[ETHERNET_ETHERTYPE_OFFSET] = ethertype << 8;
|
||||
ethernet_frame[ETHERNET_ETHERTYPE_OFFSET + 1] = ethertype & 0xFF;
|
||||
|
||||
*packet_buffer_out = ethernet_frame + ETHERNET_MAC_HEADER_LENGTH;
|
||||
|
||||
return de_index;
|
||||
}
|
||||
|
||||
U64 EthernetGetMAC()
|
||||
U8 *EthernetGetMAC()
|
||||
{
|
||||
return pcnet.mac_address;
|
||||
}
|
||||
|
||||
PCNetInit;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
488
src/Home/Net/Sockets.CC
Executable file
488
src/Home/Net/Sockets.CC
Executable file
@ -0,0 +1,488 @@
|
||||
/* docs.idris-lang.org/en/latest/st/examples.html
|
||||
beej.us/guide/bgnet/html/
|
||||
|
||||
Sockets are non-standard, a simple
|
||||
Finite State Machine. The functions'
|
||||
only args are the socket. Socket functions
|
||||
requiring more parameters should be
|
||||
defined at the protocol level.
|
||||
|
||||
The state machine exists to allow
|
||||
protocol code to execute code in
|
||||
the appropriate order. When calling
|
||||
a socket function, code can use
|
||||
the modified/unmodified states to
|
||||
determine next procedure.
|
||||
|
||||
I included some code for IPV6, currently
|
||||
unused. */
|
||||
|
||||
#define SOCKET_STATE_READY 0
|
||||
#define SOCKET_STATE_BIND_REQ 1
|
||||
#define SOCKET_STATE_CONNECT_REQ 2
|
||||
#define SOCKET_STATE_BOUND 3
|
||||
#define SOCKET_STATE_LISTEN_REQ 4
|
||||
#define SOCKET_STATE_LISTENING 5
|
||||
#define SOCKET_STATE_OPEN 6
|
||||
#define SOCKET_STATE_CLOSE_REQ 7
|
||||
#define SOCKET_STATE_CLOSED 8
|
||||
|
||||
#define SOCKET_STREAM 1
|
||||
#define SOCKET_DATAGRAM 2
|
||||
#define SOCKET_RAW 3
|
||||
|
||||
#define AF_INET 2
|
||||
#define AF_INET6 10
|
||||
|
||||
#define INET_ADDRSTRLEN 16 //pubs.opengroup.com netinit/in.h
|
||||
#define INET6_ADDRSTRLEN 46
|
||||
|
||||
#define INET_MIN_ADDRSTRLEN 7 // ex: len of 0.0.0.0
|
||||
#define INET6_MIN_ADDRSTRLEN 2 // ie: len of ::
|
||||
|
||||
|
||||
#define IP_PARSE_STATE_NUM 0
|
||||
#define IP_PARSE_STATE_DOT 1
|
||||
|
||||
|
||||
class CSocketAddress
|
||||
{
|
||||
U16 family; // 'address family, AF_xxx'
|
||||
|
||||
U8 data[14]; // '14 bytes of protocol address'
|
||||
};
|
||||
|
||||
class CIPV4Address
|
||||
{
|
||||
U32 address; // 'in Network Byte order' ... Big Endian
|
||||
};
|
||||
|
||||
class CIPV6Address
|
||||
{
|
||||
U8 address[16]; //a clear #define would be nice
|
||||
};
|
||||
|
||||
class CIPAddressStorage
|
||||
{// class specifically meant to be generic casted either IPV4 or IPV6 Address.
|
||||
U8 padding[16];
|
||||
};
|
||||
|
||||
class CSocketAddressIPV4
|
||||
{
|
||||
I16 family; // 'AF_INET'
|
||||
U16 port; // 'in Network Byte order' ... Big Endian
|
||||
|
||||
CIPV4Address address;
|
||||
|
||||
U8 zeroes[8]; //'same size as socket address'
|
||||
};
|
||||
|
||||
class CSocketAddressIPV6
|
||||
{
|
||||
U16 family; // 'AF_INET6'
|
||||
U16 port; // 'in Network Byte order'... Big Endian
|
||||
|
||||
U32 flow_info;
|
||||
|
||||
CIPV6Address address;
|
||||
|
||||
U32 scope_id;
|
||||
};
|
||||
|
||||
class CSocketAddressStorage
|
||||
{/* 'designed to be large enough to
|
||||
hold both IPV4 and IPV6 structures.' */
|
||||
|
||||
U16 family;
|
||||
|
||||
U8 padding[26];
|
||||
|
||||
};
|
||||
|
||||
class CAddressInfo
|
||||
{
|
||||
I32 flags;
|
||||
I32 family;
|
||||
I32 socket_type;
|
||||
I32 protocol;
|
||||
|
||||
I64 address_length;
|
||||
|
||||
CSocketAddress *address;
|
||||
|
||||
U8 *canonical_name;
|
||||
|
||||
CAddressInfo *next;
|
||||
};
|
||||
|
||||
class CSocket
|
||||
{
|
||||
U8 state;
|
||||
|
||||
U16 type;
|
||||
U16 domain;
|
||||
};
|
||||
|
||||
Bool IPV4AddressParse(U8 *string, U32* destination)
|
||||
{
|
||||
// U8* lexable_string;
|
||||
// lexable_string = StrReplace(string, ",", ","); // swap any commas with an unexpected value
|
||||
|
||||
U8 *lexable_string = StrReplace(string, ".", ","); // swap dots with commas since Lex is easier with them.
|
||||
|
||||
CCompCtrl* cc = CompCtrlNew(lexable_string);
|
||||
//Bts(&cc->opts, OPTf_DECIMAL_ONLY);
|
||||
|
||||
cc->opts |= 1 << OPTf_DECIMAL_ONLY;
|
||||
|
||||
I64 tk;
|
||||
|
||||
I64 state = IP_PARSE_STATE_NUM;
|
||||
U32 temp_destination = 0;
|
||||
|
||||
I64 current_section = 0; // IPV4 address has 4 total sections
|
||||
|
||||
while (tk = Lex(cc))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case IP_PARSE_STATE_NUM:
|
||||
switch (tk)
|
||||
{
|
||||
case TK_I64:
|
||||
if (cc->cur_i64 > 255 || cc->cur_i64 < 0)
|
||||
{
|
||||
ZenithErr("Invalid value, must be 0 - 255.\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (current_section > 3)
|
||||
{
|
||||
ZenithErr("IP Address can only have 4 sections.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
temp_destination |= cc->cur_i64 << (current_section * 8);
|
||||
current_section++;
|
||||
|
||||
state = IP_PARSE_STATE_DOT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("Expected decimal. \n");
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_PARSE_STATE_DOT:
|
||||
switch (tk)
|
||||
{
|
||||
case ',':
|
||||
state = IP_PARSE_STATE_NUM;
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("Expected dot. \n");
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
temp_destination = EndianU32(temp_destination); // store the address in Network Byte Order (Big-Endian)
|
||||
*destination = temp_destination;
|
||||
"\n\n%X\n\n",temp_destination;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
I64 PresentationToNetwork(I64 address_family, U8 *string, CIPAddressStorage *destination)
|
||||
{/* Converts IP string to internet address class, our inet_pton().
|
||||
Destination written as CIPV4Address or CIPV6Address depending
|
||||
on value of address_family.
|
||||
The destination address is the generic class, functions
|
||||
calling this method must cast their classes in the params.
|
||||
|
||||
TODO: test it more... clarify above comment? is casting shit needed if we do it like this?..
|
||||
if we declare the possible address classes, then just set them to where the
|
||||
destination is from param, wouldn't that suffice fully? I noticed that it wrote fine to
|
||||
a pointer to CIPV4Address without any complaints that it wasn't CIPAddressStorage .. */
|
||||
|
||||
//CCompCtrl *cc = CompCtrlNew(string);
|
||||
|
||||
CIPV4Address *ipv4_address;
|
||||
CIPV6Address *ipv6_address;
|
||||
|
||||
I64 string_length = StrLen(string);
|
||||
|
||||
switch (address_family)
|
||||
{
|
||||
case AF_INET:
|
||||
if (string_length > INET_ADDRSTRLEN || string_length < INET_MIN_ADDRSTRLEN)
|
||||
{
|
||||
ZenithErr("IP to Socket Address failed: Invalid Input String Size.\n");
|
||||
return -1;
|
||||
}
|
||||
ipv4_address = destination;
|
||||
|
||||
IPV4AddressParse(string, &ipv4_address->address);
|
||||
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (string_length > INET6_ADDRSTRLEN || string_length < INET6_MIN_ADDRSTRLEN)
|
||||
{
|
||||
ZenithErr("IP to Socket Address failed: Invalid Input String Size.\n");
|
||||
return -1;
|
||||
}
|
||||
ipv6_address = destination;
|
||||
|
||||
|
||||
Debug("IPV6 support not implemented yet.\n");
|
||||
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("IP to Socket Address failed: Invalid Address Family.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//CompCtrlDel(cc);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
U8 *NetworkToPresentation(I64 address_family, CIPAddressStorage *source)
|
||||
{ // converts socket address to IP string, our inet_ntop. Taking Shrine approach of function returns U8* .
|
||||
|
||||
// I64 i;
|
||||
|
||||
U8* ip_string;
|
||||
// U8* ip_string[INET_ADDRSTRLEN];
|
||||
CIPV4Address *ipv4_source;
|
||||
CIPV4Address *ipv6_source;
|
||||
|
||||
switch (address_family)
|
||||
{
|
||||
case AF_INET:
|
||||
|
||||
ipv4_source = source;
|
||||
|
||||
|
||||
StrPrint(ip_string, "%d.%d.%d.%d",
|
||||
ipv4_source->address.u8[3],
|
||||
ipv4_source->address.u8[2],
|
||||
ipv4_source->address.u8[1],
|
||||
ipv4_source->address.u8[0]);
|
||||
|
||||
break;
|
||||
case AF_INET6:
|
||||
|
||||
ipv6_source = source;
|
||||
|
||||
Debug("IPV6 support not implemented yet.\n");
|
||||
|
||||
break;
|
||||
default:
|
||||
ZenithErr("Socket Address to IP failed: Invalid Address Family.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return ip_string;
|
||||
}
|
||||
|
||||
|
||||
CSocket *Socket(U16 domain, U16 type)
|
||||
{
|
||||
CSocket *socket = CAlloc(sizeof(CSocket));
|
||||
|
||||
socket->domain = domain;
|
||||
socket->type = type;
|
||||
|
||||
socket->state = SOCKET_STATE_READY;
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
U0 SocketStateErr(U8 *request, U8 state)
|
||||
{
|
||||
U8 *state_string;
|
||||
switch (state)
|
||||
{
|
||||
case SOCKET_STATE_READY:
|
||||
state_string = StrNew("READY");
|
||||
break;
|
||||
case SOCKET_STATE_BIND_REQ:
|
||||
state_string = StrNew("BIND REQUEST");
|
||||
break;
|
||||
case SOCKET_STATE_CONNECT_REQ:
|
||||
state_string = StrNew("CONNECT REQUEST");
|
||||
break;
|
||||
case SOCKET_STATE_BOUND:
|
||||
state_string = StrNew("BOUND");
|
||||
break;
|
||||
case SOCKET_STATE_LISTEN_REQ:
|
||||
state_string = StrNew("LISTEN REQUEST");
|
||||
break;
|
||||
case SOCKET_STATE_LISTENING:
|
||||
state_string = StrNew("LISTENING");
|
||||
break;
|
||||
case SOCKET_STATE_OPEN:
|
||||
state_string = StrNew("OPEN");
|
||||
break;
|
||||
case SOCKET_STATE_CLOSE_REQ:
|
||||
state_string = StrNew("CLOSE REQUEST");
|
||||
break;
|
||||
case SOCKET_STATE_CLOSED:
|
||||
state_string = StrNew("CLOSED");
|
||||
break;
|
||||
}
|
||||
ZenithErr("Socket attempted %s while in %s state.\n", request, state_string);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
U0 SocketAccept(CSocket *socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_LISTENING:
|
||||
/* Socket expected to stay listening.
|
||||
At protocol level, a new socket 'connected'
|
||||
to this one is expected to be made. */
|
||||
return;
|
||||
|
||||
default:
|
||||
SocketStateErr("ACCEPT", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketClose(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_LISTENING:
|
||||
case SOCKET_STATE_OPEN:
|
||||
/* Sockets can only be closed if
|
||||
they were opened or listening
|
||||
to incoming connections. */
|
||||
socket->state = SOCKET_STATE_CLOSE_REQ;
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("CLOSE", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketBind(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_READY:
|
||||
/* Sockets can only be bound
|
||||
if they are in initial state. */
|
||||
socket->state = SOCKET_STATE_BIND_REQ;
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("BIND", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketConnect(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_READY:
|
||||
/* Sockets can only be connected
|
||||
if they are in initial state. */
|
||||
socket->state = SOCKET_STATE_CONNECT_REQ;
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("CONNECT", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketListen(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_BOUND:
|
||||
/* A socket must be bound to
|
||||
set it to listening. */
|
||||
socket->state = SOCKET_STATE_LISTEN_REQ;
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("LISTEN", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketReceive(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_OPEN:
|
||||
/* Sockets can only send/recv when
|
||||
they have been connected to. */
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("RECEIVE", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketReceiveFrom(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_OPEN:
|
||||
/* Sockets can only send/recv when
|
||||
they have been connected to. */
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("RECEIVE FROM", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketSend(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_OPEN:
|
||||
/* Sockets can only send/recv when
|
||||
they have been connected to. */
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("SEND", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U0 SocketSendTo(CSocket* socket)
|
||||
{
|
||||
switch (socket->state)
|
||||
{
|
||||
case SOCKET_STATE_OPEN:
|
||||
/* Sockets can only send/recv when
|
||||
they have been connected to. */
|
||||
break;
|
||||
|
||||
default:
|
||||
SocketStateErr("SEND TO", socket->state);
|
||||
break;
|
||||
}
|
||||
}
|
94
src/Home/Net/Tests/Test.CC
Executable file
94
src/Home/Net/Tests/Test.CC
Executable file
@ -0,0 +1,94 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//#define IP_PARSE_STATE_FIRST_NUM 0
|
||||
#define IP_PARSE_STATE_NUM 1
|
||||
#define IP_PARSE_STATE_DOT 2
|
||||
#define IP_PARSE_STATE_ERROR 3
|
||||
|
||||
|
||||
I64 IPV4AddressParse(U8 *string, U32 destination)
|
||||
{//destination output in network order, only write on success
|
||||
|
||||
I64 i;
|
||||
I64 parse_state = IP_PARSE_STATE_NUM;
|
||||
U32 temp_destination = 0;
|
||||
|
||||
I64 current_chunk_index = 0;
|
||||
U8 *digit_buffer = StrNew("");
|
||||
|
||||
// chunk is just IPV4 num 0 through 3. on last chunk, don't go to DOT state.
|
||||
|
||||
for (i = 0; i < 16; i++) //use the #define in Sockets
|
||||
{
|
||||
switch (parse_state)
|
||||
{
|
||||
case IP_PARSE_STATE_NUM:
|
||||
switch (string[i])
|
||||
{
|
||||
case '.':
|
||||
parse_state = IP_PARSE_STATE_DOT;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
StrPrint(digit_buffer, "%s%c", digit_buffer, string[i]);
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("IPV4 Parse Failure: Unexpected char.\n");
|
||||
parse_state = IP_PARSE_STATE_ERROR;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case IP_PARSE_STATE_DOT:
|
||||
"%s, %X\n", digit_buffer, Str2I64(digit_buffer);
|
||||
if (Str2I64(digit_buffer) > 255)
|
||||
{
|
||||
ZenithErr("IPV4 Parse Failure: Chunk exceeds 0 - 255 range.\n");
|
||||
parse_state = IP_PARSE_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
temp_destination |= Str2I64(digit_buffer) << (current_chunk_index * 8);
|
||||
|
||||
StrCopy(digit_buffer, ""); // clear digit buffer
|
||||
|
||||
|
||||
current_chunk_index++;
|
||||
if (current_chunk_index > 3)
|
||||
{
|
||||
ZenithErr("IPV4 Parse Failure: Too many dots in address string.\n");
|
||||
parse_state = IP_PARSE_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
parse_state = IP_PARSE_STATE_NUM;
|
||||
|
||||
break;
|
||||
|
||||
case IP_PARSE_STATE_ERROR:
|
||||
ZenithErr("IPV4 Parse Failure: Invalid Address String.\n");
|
||||
return -1; // error state!
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
}
|
220
src/Home/Net/Tests/Test2.CC
Executable file
220
src/Home/Net/Tests/Test2.CC
Executable file
@ -0,0 +1,220 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//#define IP_PARSE_STATE_FIRST_NUM 0
|
||||
#define IP_PARSE_STATE_NUM 1
|
||||
#define IP_PARSE_STATE_DOT 2
|
||||
#define IP_PARSE_STATE_ERROR 3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
U8 *StrDotReplace(U8* source_string)
|
||||
{
|
||||
I64 i;
|
||||
|
||||
U8 *result = StrNew(source_string);
|
||||
|
||||
for (i = 0; i < StrLen(result); i++)
|
||||
{
|
||||
if (result[i] == ',')
|
||||
{ // we're using commas internally for Lexing.
|
||||
result[i] = '.';
|
||||
} // so if we see a comma, set to dot, break lexer.
|
||||
else if (result[i] == '.')
|
||||
{ // Lex eats '.' as F64. Comma will split Lex tokens
|
||||
result[i] = ',';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
Bool IPV4AddressParse(U8 *string, U32* destination)
|
||||
{
|
||||
// U8* lexable_string;
|
||||
// lexable_string = StrReplace(string, ",", ","); // swap any commas with an unexpected value
|
||||
|
||||
U8 *lexable_string = StrReplace(string, ".", ","); // swap dots with commas since Lex is easier with them.
|
||||
|
||||
CCompCtrl* cc = CompCtrlNew(lexable_string);
|
||||
//Bts(&cc->opts, OPTf_DECIMAL_ONLY);
|
||||
|
||||
cc->opts |= 1 << OPTf_DECIMAL_ONLY;
|
||||
|
||||
I64 tk;
|
||||
|
||||
I64 state = IP_PARSE_STATE_NUM;
|
||||
U32 temp_destination = 0;
|
||||
|
||||
I64 current_section = 0; // IPV4 address has 4 total sections
|
||||
|
||||
while (tk = Lex(cc))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case IP_PARSE_STATE_NUM:
|
||||
switch (tk)
|
||||
{
|
||||
case TK_I64:
|
||||
if (cc->cur_i64 > 255 || cc->cur_i64 < 0)
|
||||
{
|
||||
ZenithErr("Invalid value, must be 0 - 255.\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (current_section > 3)
|
||||
{
|
||||
ZenithErr("IP Address can only have 4 sections.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
temp_destination |= cc->cur_i64 << (current_section * 8);
|
||||
current_section++;
|
||||
|
||||
state = IP_PARSE_STATE_DOT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("Expected decimal. \n");
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_PARSE_STATE_DOT:
|
||||
switch (tk)
|
||||
{
|
||||
case ',':
|
||||
state = IP_PARSE_STATE_NUM;
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("Expected dot. \n");
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
temp_destination = EndianU32(temp_destination); // store the address in Network Byte Order (Big-Endian)
|
||||
*destination = temp_destination;
|
||||
"\n\n%X\n\n",temp_destination;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
I64 IPV4AddressParseOOOOOOOOOOPS(U8 *string, U32 destination)
|
||||
{//destination output in network order, only write on success
|
||||
|
||||
I64 i;
|
||||
I64 parse_state = IP_PARSE_STATE_NUM;
|
||||
U32 temp_destination = 0;
|
||||
|
||||
I64 current_chunk_index = 0;
|
||||
U8 *digit_buffer = StrNew("");
|
||||
destination = 'nigggg';
|
||||
|
||||
// chunk is just IPV4 num 0 through 3. on last chunk, don't go to DOT state.
|
||||
|
||||
for (i = 0; i < 16; i++) //use the #define in Sockets
|
||||
{
|
||||
switch (parse_state)
|
||||
{
|
||||
case IP_PARSE_STATE_NUM:
|
||||
switch (string[i])
|
||||
{
|
||||
case '.':
|
||||
parse_state = IP_PARSE_STATE_DOT;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
StrPrint(digit_buffer, "%s%c", digit_buffer, string[i]);
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("IPV4 Parse Failure: Unexpected char.\n");
|
||||
parse_state = IP_PARSE_STATE_ERROR;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case IP_PARSE_STATE_DOT:
|
||||
"%s, %X\n", digit_buffer, Str2I64(digit_buffer);
|
||||
if (Str2I64(digit_buffer) > 255)
|
||||
{
|
||||
ZenithErr("IPV4 Parse Failure: Chunk exceeds 0 - 255 range.\n");
|
||||
parse_state = IP_PARSE_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
temp_destination |= Str2I64(digit_buffer) << (current_chunk_index * 8);
|
||||
|
||||
StrCopy(digit_buffer, ""); // clear digit buffer
|
||||
|
||||
|
||||
current_chunk_index++;
|
||||
if (current_chunk_index > 3)
|
||||
{
|
||||
ZenithErr("IPV4 Parse Failure: Too many dots in address string.\n");
|
||||
parse_state = IP_PARSE_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
parse_state = IP_PARSE_STATE_NUM;
|
||||
|
||||
break;
|
||||
|
||||
case IP_PARSE_STATE_ERROR:
|
||||
ZenithErr("IPV4 Parse Failure: Invalid Address String.\n");
|
||||
return -1; // error state!
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
}
|
BIN
src/Home/Net/Tests/Test3.CC
Executable file
BIN
src/Home/Net/Tests/Test3.CC
Executable file
Binary file not shown.
BIN
src/Home/Net/Tests/Test4.CC
Executable file
BIN
src/Home/Net/Tests/Test4.CC
Executable file
Binary file not shown.
BIN
src/Home/Net/Tests/Test5.CC
Executable file
BIN
src/Home/Net/Tests/Test5.CC
Executable file
Binary file not shown.
90
src/Home/Net/Tests/Test6.CC
Executable file
90
src/Home/Net/Tests/Test6.CC
Executable file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
CUDPSocket *s = UDPSocket(AF_INET);
|
||||
|
||||
CSocketAddressIPV4 *addr = CAlloc(sizeof(CSocketAddressIPV4));
|
||||
|
||||
ClassRep(addr);
|
||||
Class:"CSocketAddressIPV4"
|
||||
15A984E30 $FG,2$family :$FG$0000
|
||||
15A984E32 $FG,2$port :$FG$0000
|
||||
15A984E34 $TR-C,"address"$
|
||||
$ID,2$Class:"CIPV4Address"
|
||||
15A984E34 $FG,2$address :$FG$00000000
|
||||
$ID,-2$15A984E38 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
|
||||
0.000360s
|
||||
addr->family = AF_INET;
|
||||
0.000006s ans=0x00000002=2
|
||||
addr->port = EndianU16(0xDEAF);
|
||||
0.000007s ans=0x0000AFDE=45022
|
||||
PresentationToNetwork(AF_INET,"102.33.6.1",&addr->address);
|
||||
|
||||
|
||||
66210601
|
||||
|
||||
0.000027s ans=0x00000000=0
|
||||
ClassRep(addr);
|
||||
Class:"CSocketAddressIPV4"
|
||||
15A984E30 $FG,2$family :$FG$0002
|
||||
15A984E32 $FG,2$port :$FG$AFDE
|
||||
15A984E34 $TR-C,"address"$
|
||||
$ID,2$Class:"CIPV4Address"
|
||||
15A984E34 $FG,2$address :$FG$66210601
|
||||
$ID,-2$15A984E38 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
|
||||
0.000385s
|
||||
UDPSocketBind(s, addr);
|
||||
0.000008s ans=0x00000000=0
|
||||
ClassRep(s);
|
||||
Class:"CUDPSocket"
|
||||
15A982C58 $TR,"socket"$
|
||||
$ID,2$Class:"CSocket"
|
||||
15A9931E8 $FG,2$state :$FG$03
|
||||
15A9931E9 $FG,2$type :$FG$0002
|
||||
15A9931EB $FG,2$domain :$FG$0002
|
||||
$ID,-2$15A982C60 $FG,2$receive_timeout_ms :$FG$0000000000000000
|
||||
15A982C68 $FG,2$receive_max_timeout :$FG$0000000000000000
|
||||
15A982C70 $FG,2$receive_buffer :$FG$
|
||||
15A982C78 $FG,2$receive_len :$FG$0000000000000000
|
||||
15A982C80 $TR-C,"receive_address"$
|
||||
$ID,2$Class:"CSocketAddressIPV4"
|
||||
15A982C80 $FG,2$family :$FG$0002
|
||||
15A982C82 $FG,2$port :$FG$0000
|
||||
15A982C84 $TR,"address"$
|
||||
$ID,2$$ID,-2$15A982C88 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
$ID,-2$15A982C90 $FG,2$bound_to :$FG$DEAF
|
||||
|
||||
0.000526s
|
||||
ClassRep(&s->receive_address);
|
||||
Class:"CSocketAddressIPV4"
|
||||
15A982C80 $FG,2$family :$FG$0002
|
||||
15A982C82 $FG,2$port :$FG$0000
|
||||
15A982C84 $TR-C,"address"$
|
||||
$ID,2$Class:"CIPV4Address"
|
||||
15A982C84 $FG,2$address :$FG$66210601
|
||||
$ID,-2$15A982C88 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
|
||||
0.000323s
|
||||
|
||||
*/
|
||||
|
||||
CUDPSocket *s = UDPSocket(AF_INET);
|
||||
CSocketAddressIPV4 *addr = CAlloc(sizeof(CSocketAddressIPV4));
|
||||
|
||||
ClassRep(s);
|
||||
ClassRep(addr);
|
||||
|
||||
"\nSetting addr family to AF_INET and port to 0xDEAF in B.E., then P to N with 102.33.6.1\n";
|
||||
addr->family = AF_INET;
|
||||
addr->port = EndianU16(0xDEAF);
|
||||
PresentationToNetwork(AF_INET,"102.33.6.1",&addr->address);
|
||||
|
||||
ClassRep(addr);
|
||||
|
||||
"\nUDPSocket bind with socket to addr\n";
|
||||
UDPSocketBind(s, addr);
|
||||
|
||||
ClassRep(s);
|
||||
ClassRep(&s->receive_address);
|
||||
|
||||
UDPSocketClose(s);
|
129
src/Home/Net/Tests/Test7.CC
Executable file
129
src/Home/Net/Tests/Test7.CC
Executable file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
CUDPSocket *s = UDPSocket(AF_INET);
|
||||
|
||||
CSocketAddressIPV4 *addr = CAlloc(sizeof(CSocketAddressIPV4));
|
||||
|
||||
ClassRep(addr);
|
||||
Class:"CSocketAddressIPV4"
|
||||
15A984E30 $FG,2$family :$FG$0000
|
||||
15A984E32 $FG,2$port :$FG$0000
|
||||
15A984E34 $TR-C,"address"$
|
||||
$ID,2$Class:"CIPV4Address"
|
||||
15A984E34 $FG,2$address :$FG$00000000
|
||||
$ID,-2$15A984E38 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
|
||||
0.000360s
|
||||
addr->family = AF_INET;
|
||||
0.000006s ans=0x00000002=2
|
||||
addr->port = EndianU16(0xDEAF);
|
||||
0.000007s ans=0x0000AFDE=45022
|
||||
PresentationToNetwork(AF_INET,"102.33.6.1",&addr->address);
|
||||
|
||||
|
||||
66210601
|
||||
|
||||
0.000027s ans=0x00000000=0
|
||||
ClassRep(addr);
|
||||
Class:"CSocketAddressIPV4"
|
||||
15A984E30 $FG,2$family :$FG$0002
|
||||
15A984E32 $FG,2$port :$FG$AFDE
|
||||
15A984E34 $TR-C,"address"$
|
||||
$ID,2$Class:"CIPV4Address"
|
||||
15A984E34 $FG,2$address :$FG$66210601
|
||||
$ID,-2$15A984E38 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
|
||||
0.000385s
|
||||
UDPSocketBind(s, addr);
|
||||
0.000008s ans=0x00000000=0
|
||||
ClassRep(s);
|
||||
Class:"CUDPSocket"
|
||||
15A982C58 $TR,"socket"$
|
||||
$ID,2$Class:"CSocket"
|
||||
15A9931E8 $FG,2$state :$FG$03
|
||||
15A9931E9 $FG,2$type :$FG$0002
|
||||
15A9931EB $FG,2$domain :$FG$0002
|
||||
$ID,-2$15A982C60 $FG,2$receive_timeout_ms :$FG$0000000000000000
|
||||
15A982C68 $FG,2$receive_max_timeout :$FG$0000000000000000
|
||||
15A982C70 $FG,2$receive_buffer :$FG$
|
||||
15A982C78 $FG,2$receive_len :$FG$0000000000000000
|
||||
15A982C80 $TR-C,"receive_address"$
|
||||
$ID,2$Class:"CSocketAddressIPV4"
|
||||
15A982C80 $FG,2$family :$FG$0002
|
||||
15A982C82 $FG,2$port :$FG$0000
|
||||
15A982C84 $TR,"address"$
|
||||
$ID,2$$ID,-2$15A982C88 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
$ID,-2$15A982C90 $FG,2$bound_to :$FG$DEAF
|
||||
|
||||
0.000526s
|
||||
ClassRep(&s->receive_address);
|
||||
Class:"CSocketAddressIPV4"
|
||||
15A982C80 $FG,2$family :$FG$0002
|
||||
15A982C82 $FG,2$port :$FG$0000
|
||||
15A982C84 $TR-C,"address"$
|
||||
$ID,2$Class:"CIPV4Address"
|
||||
15A982C84 $FG,2$address :$FG$66210601
|
||||
$ID,-2$15A982C88 $FG,2$zeroes :$FG$00 00 00 00 00 00 00 00
|
||||
|
||||
0.000323s
|
||||
|
||||
*/
|
||||
|
||||
"\nMaking new socket\n";
|
||||
CUDPSocket *s = UDPSocket(AF_INET);
|
||||
CSocketAddressIPV4 *addr = CAlloc(sizeof(CSocketAddressIPV4));
|
||||
|
||||
ClassRep(s);
|
||||
ClassRep(addr);
|
||||
|
||||
"\nSetting addr family to AF_INET and port to 0xDEAF in B.E., then P to N with 102.33.6.1\n";
|
||||
addr->family = AF_INET;
|
||||
addr->port = EndianU16(0xDEAF);
|
||||
PresentationToNetwork(AF_INET,"102.33.6.1",&addr->address);
|
||||
|
||||
ClassRep(addr);
|
||||
|
||||
"\nUDPSocket bind with socket to addr\n";
|
||||
UDPSocketBind(s, addr);
|
||||
|
||||
ClassRep(s);
|
||||
ClassRep(&s->receive_address);
|
||||
|
||||
"\nGlobal Tree:\n";
|
||||
ClassRep(udp_globals.bound_socket_tree);
|
||||
"\nGlobal Tree: Port 0xDEAF Queue\n";
|
||||
ClassRep(udp_globals.bound_socket_tree->queue);
|
||||
|
||||
///
|
||||
|
||||
"\nMaking new socket\n";
|
||||
CUDPSocket *s2 = UDPSocket(AF_INET);
|
||||
CSocketAddressIPV4 *addr2 = CAlloc(sizeof(CSocketAddressIPV4));
|
||||
|
||||
ClassRep(s2);
|
||||
ClassRep(addr2);
|
||||
|
||||
"\nSetting addr2 family to AF_INET and port to 0xDEAF in B.E., then P to N with 104.32.3.66\n";
|
||||
addr2->family = AF_INET;
|
||||
addr2->port = EndianU16(0xDEAF);
|
||||
PresentationToNetwork(AF_INET,"104.32.3.66",&addr2->address);
|
||||
|
||||
ClassRep(addr2);
|
||||
|
||||
"\nUDPSocket bind with socket2 to addr2\n";
|
||||
UDPSocketBind(s2, addr2);
|
||||
|
||||
ClassRep(s2);
|
||||
ClassRep(&s2->receive_address);
|
||||
|
||||
"\nGlobal Tree:\n";
|
||||
ClassRep(udp_globals.bound_socket_tree);
|
||||
"\nGlobal Tree: Port 0xDEAF Queue\n";
|
||||
ClassRep(udp_globals.bound_socket_tree->queue);
|
||||
|
||||
"\nClosing first socket\n";
|
||||
UDPSocketClose(s);
|
||||
|
||||
"\nGlobal Tree:\n";
|
||||
ClassRep(udp_globals.bound_socket_tree);
|
||||
"\nGlobal Tree: Port 0xDEAF Queue\n";
|
||||
ClassRep(udp_globals.bound_socket_tree->queue);
|
614
src/Home/Net/UDP.CC
Executable file
614
src/Home/Net/UDP.CC
Executable file
@ -0,0 +1,614 @@
|
||||
//#include "IPV4"
|
||||
#include "ICMP" // this is wrong and only doing this because we're just in dev right now. probably need approach like Shrine, MakeNet, idk.
|
||||
#include "Sockets"
|
||||
|
||||
#define UDP_MAX_PORT 65535
|
||||
|
||||
class CUDPHeader
|
||||
{
|
||||
U16 source_port;
|
||||
U16 destination_port;
|
||||
|
||||
U16 length;
|
||||
U16 checksum;
|
||||
};
|
||||
|
||||
class CUDPSocket
|
||||
{
|
||||
CSocket* socket;
|
||||
|
||||
I64 receive_timeout_ms;
|
||||
I64 receive_max_timeout;
|
||||
|
||||
U8 *receive_buffer;
|
||||
I64 receive_len;
|
||||
|
||||
CSocketAddressIPV4 receive_address; // should this change to Storage class for IPV6 later ?
|
||||
|
||||
U16 bound_to; // represents the currently bound port
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// UDP Bound Socket Tree Classes & Functions
|
||||
|
||||
class CUDPTreeQueue
|
||||
{ // next, last for CQueue implementation.
|
||||
CUDPTreeQueue *next;
|
||||
CUDPTreeQueue *last;
|
||||
|
||||
CUDPSocket* socket;
|
||||
};
|
||||
|
||||
class CUDPTreeNode
|
||||
{
|
||||
I64 port;
|
||||
|
||||
CUDPTreeNode *left;
|
||||
CUDPTreeNode *right;
|
||||
|
||||
CUDPTreeQueue* queue;
|
||||
};
|
||||
|
||||
CUDPTreeNode *UDPTreeNodeInit()
|
||||
{ // init new empty tree/node.
|
||||
CUDPTreeNode *tree_node = CAlloc(sizeof(CUDPTreeNode));
|
||||
|
||||
return tree_node;
|
||||
}
|
||||
|
||||
U0 UDPTreeNodeAdd(CUDPTreeNode *node, CUDPTreeNode *tree)
|
||||
{ // using temp and last allows avoiding recursion and non-growing stack issues.
|
||||
CUDPTreeNode *temp_tree = tree;
|
||||
CUDPTreeNode *last_tree = temp_tree;
|
||||
|
||||
while (temp_tree)
|
||||
{
|
||||
if (node->port < temp_tree->port)
|
||||
{ // if node smaller, go left
|
||||
last_tree = temp_tree;
|
||||
temp_tree = temp_tree->left;
|
||||
}
|
||||
else
|
||||
{ // if node equal or larger, go right
|
||||
last_tree = temp_tree;
|
||||
temp_tree = temp_tree->right;
|
||||
} // at the end of this, this _should_ result in last_tree
|
||||
} // being the resulting tree to store the node inside of. i guess recompute the direction and set.
|
||||
|
||||
if (node->port < last_tree->port)// if node smaller, go left
|
||||
last_tree->left = node;
|
||||
else // if node equal or larger, go right
|
||||
last_tree->right = node;
|
||||
}
|
||||
|
||||
CUDPTreeNode *UDPTreeNodeParamAdd(I64 node_port, CUDPTreeNode *tree)
|
||||
{ // add a node using params, return pointer to the node
|
||||
CUDPTreeNode *result = UDPTreeNodeInit;
|
||||
result->port = node_port;
|
||||
|
||||
UDPTreeNodeAdd(result, tree);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CUDPTreeNode *UDPTreeNodeParamInit(I64 port)
|
||||
{
|
||||
CUDPTreeNode *result = UDPTreeNodeInit;
|
||||
result->port = port;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CUDPTreeNode *UDPTreeNodeFind(I64 port, CUDPTreeNode *tree)
|
||||
{
|
||||
CUDPTreeNode *temp_tree = tree;
|
||||
|
||||
while (temp_tree)
|
||||
{
|
||||
if (port < temp_tree->port) // if value smaller, go left
|
||||
temp_tree = temp_tree->left;
|
||||
else if (port > temp_tree->port) // if value larger, go right
|
||||
temp_tree = temp_tree->right;
|
||||
else // if value equal, match! i guess?
|
||||
break;
|
||||
}
|
||||
return temp_tree; // ! NULL if not found.
|
||||
}
|
||||
|
||||
CUDPTreeNode *UDPTreeNodePop(I64 port, CUDPTreeNode *tree)
|
||||
{ // mimics TreeNodeFind. pops whole sub-tree, original tree loses whole branch.
|
||||
CUDPTreeNode *parent_tree = tree;
|
||||
CUDPTreeNode *temp_tree = parent_tree;
|
||||
Bool is_left = FALSE;
|
||||
Bool is_right = FALSE;
|
||||
|
||||
while (temp_tree)
|
||||
{
|
||||
if (port < temp_tree->port)
|
||||
{
|
||||
parent_tree = temp_tree;
|
||||
temp_tree = temp_tree->left;
|
||||
is_right = FALSE;
|
||||
is_left = TRUE;
|
||||
}
|
||||
else if (port > temp_tree->port)
|
||||
{
|
||||
parent_tree = temp_tree;
|
||||
temp_tree = temp_tree->right;
|
||||
is_right = TRUE;
|
||||
is_left = FALSE;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (temp_tree)
|
||||
{ //if we found it, clear its parents link to the node
|
||||
if (is_left)
|
||||
{
|
||||
parent_tree->left = NULL;
|
||||
}
|
||||
else if (is_right)
|
||||
{
|
||||
parent_tree->right = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return temp_tree; // NULL if not found.
|
||||
}
|
||||
|
||||
CUDPTreeNode *UDPTreeNodeSinglePop(I64 port, CUDPTreeNode *tree)
|
||||
{ // pop whole sub-tree, then add back in its sub-trees. TODO: should we leave the pointers in the node or clear them ?
|
||||
CUDPTreeNode *node = UDPTreeNodePop(port, tree);
|
||||
CUDPTreeNode *left = node->left;
|
||||
CUDPTreeNode *right = node->right;
|
||||
|
||||
if (node)
|
||||
{
|
||||
if (left)
|
||||
{
|
||||
UDPTreeNodeAdd(left, tree);
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
UDPTreeNodeAdd(right, tree);
|
||||
}
|
||||
}
|
||||
|
||||
// ... see the TODO ^
|
||||
// node->left = NULL;
|
||||
// node->right = NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
U0 UDPTreeNodeFree(CUDPTreeNode *node)
|
||||
{ // only clears and frees the node. !! if node has subtrees, they will be left floating. use with caution to avoid memory leaks
|
||||
// ... uh.. what to do with the inner CTreeQueue floating around ..? we need to fix that too right?
|
||||
// .. what does CQueue functions give us. QueueRemove is our best bet, i guess it will just try to swap around the next last ptrs.
|
||||
}
|
||||
|
||||
U0 UDPTreeNodeQueueInit(CUDPTreeNode *node)
|
||||
{
|
||||
node->queue = CAlloc(sizeof(CUDPTreeQueue));
|
||||
QueueInit(node->queue);
|
||||
}
|
||||
|
||||
U0 UDPTreeNodeQueueAdd(CUDPSocket* socket, CUDPTreeNode *node)
|
||||
{
|
||||
CUDPTreeQueue *new_entry;
|
||||
|
||||
if (!node->queue)
|
||||
{
|
||||
UDPTreeNodeQueueInit(node);
|
||||
node->queue->socket = socket;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_entry = CAlloc(sizeof(CUDPTreeQueue));
|
||||
QueueInit(new_entry);
|
||||
new_entry->socket = socket;
|
||||
QueueInsert(new_entry, node->queue->last);
|
||||
}
|
||||
}
|
||||
|
||||
// refactored to UDPTreeNodeQueueSocketFind for Socket-call level functions9
|
||||
CUDPTreeQueue *UDPTreeNodeQueueSocketFind(CUDPSocket* socket, CUDPTreeNode *node)
|
||||
{
|
||||
|
||||
CUDPTreeQueue *temp_queue;
|
||||
|
||||
if (node->queue)
|
||||
{
|
||||
|
||||
if (node->queue->socket == socket)
|
||||
return node->queue;
|
||||
|
||||
temp_queue = node->queue->next;
|
||||
while (temp_queue != node->queue)
|
||||
{
|
||||
if (temp_queue->socket == socket)
|
||||
return temp_queue;
|
||||
temp_queue = temp_queue->next;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CUDPTreeQueue *UDPTreeNodeQueueFind(U32 address, CUDPTreeNode *node)
|
||||
{ // address should be pulled from an instance of CIPV4Address (todo.. double check what bit order we're in ?)
|
||||
|
||||
CUDPTreeQueue *temp_queue;
|
||||
|
||||
if (node->queue)
|
||||
{
|
||||
if (node->queue->socket->receive_address.address == address)
|
||||
return node->queue;
|
||||
|
||||
temp_queue = node->queue->next;
|
||||
while (temp_queue != node->queue)
|
||||
{
|
||||
if (temp_queue->socket->receive_address.address == address)
|
||||
return temp_queue;
|
||||
|
||||
temp_queue = temp_queue->next;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
CUDPTreeQueue *UDPTreeNodeQueueSocketSinglePop(CUDPSocket *socket, CUDPTreeNode *node)
|
||||
{ // search by socket, pop a single UDPTreeQueue off the node, return popped queue.
|
||||
CUDPTreeQueue *temp_queue = UDPTreeNodeQueueSocketFind(socket, node);
|
||||
|
||||
if (temp_queue)
|
||||
QueueRemove(temp_queue); // links between queue entries pop out this and stitch back together. popped entry might have old links?
|
||||
|
||||
return temp_queue; // if not found, NULL.
|
||||
}
|
||||
|
||||
|
||||
CUDPTreeQueue *UDPTreeNodeQueueSinglePop(U32 address, CUDPTreeNode *node)
|
||||
{ // pop a single UDPTreeQueue off the node, return popped queue.
|
||||
CUDPTreeQueue *temp_queue = UDPTreeNodeQueueFind(address, node);
|
||||
|
||||
if (temp_queue)
|
||||
QueueRemove(temp_queue); // links between queue entries pop out this and stitch back together. popped entry might have old links?
|
||||
|
||||
return temp_queue; // if not found, NULL.
|
||||
}
|
||||
|
||||
// end UDP Bound Socket functions & classes
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CUDPGlobals
|
||||
{
|
||||
|
||||
CUDPTreeNode* bound_socket_tree;
|
||||
|
||||
} udp_globals;
|
||||
|
||||
|
||||
U0 UDPGlobalsInit()
|
||||
{
|
||||
udp_globals.bound_socket_tree = NULL;
|
||||
}
|
||||
|
||||
I64 UDPPacketAllocate(U8** frame_out,
|
||||
U32 source_ip,
|
||||
U16 source_port,
|
||||
U32 destination_ip,
|
||||
U16 destination_port,
|
||||
I64 length)
|
||||
{
|
||||
U8 *ethernet_frame;
|
||||
I64 de_index;
|
||||
CUDPHeader* header;
|
||||
|
||||
de_index = IPV4PacketAllocate(ðernet_frame,
|
||||
IP_PROTOCOL_UDP,
|
||||
source_ip,
|
||||
destination_ip,
|
||||
sizeof(CUDPHeader) + length);
|
||||
if (de_index < 0)
|
||||
{
|
||||
ZenithLog("UDP Ethernet Frame Allocate failed.\n");
|
||||
return de_index;
|
||||
}
|
||||
|
||||
header = ethernet_frame;
|
||||
|
||||
header->source_port = EndianU16(source_port);
|
||||
header->destination_port = EndianU16(destination_port);
|
||||
header->length = EndianU16(sizeof(CUDPHeader) + length);
|
||||
header->checksum = 0;
|
||||
|
||||
*frame_out = ethernet_frame + sizeof(CUDPHeader);
|
||||
|
||||
}
|
||||
|
||||
U0 UDPPacketFinish(I64 de_index)
|
||||
{ // alias for IPV4PacketFinish, alias for EthernetFrameFinish, alias for driver send packet
|
||||
IPV4PacketFinish(de_index);
|
||||
}
|
||||
|
||||
I64 UDPParsePacket(U16 *source_port_out,
|
||||
U16 *destination_port_out,
|
||||
U8 **data_out,
|
||||
I64 *length_out,
|
||||
CIPV4Packet *packet)
|
||||
{
|
||||
|
||||
// check ip protocol? probably redundant
|
||||
|
||||
CUDPHeader *header = packet->data;
|
||||
|
||||
// Shrine has FIXME, validate packet length!
|
||||
|
||||
*source_port_out = EndianU16(header->source_port);
|
||||
*destination_port_out = EndianU16(header->destination_port);
|
||||
|
||||
*data_out = packet->data + sizeof(CUDPHeader);
|
||||
*length_out = packet->length - sizeof(CUDPHeader);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//CUDPSocket *UDPSocket(U16 domain, U16 type) // should this even be allowed? why not just UDPSocket; ? it could just know its domain and type.
|
||||
CUDPSocket *UDPSocket(U16 domain)
|
||||
{
|
||||
U16 type = SOCKET_DATAGRAM;
|
||||
|
||||
if (domain != AF_INET)
|
||||
Debug("Non IPV4 UDP Sockets not implemented yet !\n");
|
||||
|
||||
// if (type != SOCKET_DATAGRAM)
|
||||
// Debug("UDP Sockets must be of type SOCKET DATAGRAM"); // maybe just return null if wrong type and ZenithErr
|
||||
|
||||
CUDPSocket *udp_socket = CAlloc(sizeof(CUDPSocket));
|
||||
|
||||
udp_socket->socket = Socket(domain, type);
|
||||
|
||||
udp_socket->receive_address.family = domain; // should be INET (or INET6 i guess)
|
||||
|
||||
return udp_socket;
|
||||
|
||||
}
|
||||
|
||||
I64 UDPSocketBind(CUDPSocket *udp_socket, CSocketAddress *address_in) // I64 addr_len ?? does it really matter that much
|
||||
{
|
||||
//if we put in addr len do a check its valid for ipv4 and ipv6 based on family
|
||||
|
||||
CUDPTreeNode *temp_node;
|
||||
CSocketAddressIPV4 *ipv4_socket_addr;
|
||||
U16 port;
|
||||
|
||||
switch (udp_socket->socket->state)
|
||||
{
|
||||
case SOCKET_STATE_READY: // Socket State machine must be in init state
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("Unsuccessful UDP Socket Bind: Socket state-machine must be in READY state.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (udp_socket->bound_to)
|
||||
{
|
||||
ZenithErr("Attempted UDP Socket Bind while UDP socket currently bound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (address_in->family != AF_INET) Debug("Non IPV4 socket binds not implemented !");
|
||||
|
||||
ipv4_socket_addr = address_in;
|
||||
udp_socket->receive_address.address.address = ipv4_socket_addr->address.address; // bind socket to address in parameter.
|
||||
udp_socket->receive_address.port = ipv4_socket_addr->port; // ... consistency would say keep in Big Endian ...
|
||||
|
||||
port = EndianU16(ipv4_socket_addr->port); // port member should be Big Endian, so now we're going L.E (?)
|
||||
|
||||
if (udp_globals.bound_socket_tree)
|
||||
{
|
||||
// look for our port.
|
||||
temp_node = UDPTreeNodeFind(port, udp_globals.bound_socket_tree);
|
||||
|
||||
if (temp_node)
|
||||
{ // if we find we have bound sockets at port, check address before adding to queue
|
||||
if (UDPTreeNodeQueueFind(udp_socket->receive_address.address.address, temp_node))
|
||||
{
|
||||
ZenithErr("Attempted UDP Socket Bind at an address already in Bound Socket Tree !\n");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{ // if no address match, free to add socket to the node queue
|
||||
UDPTreeNodeQueueAdd(udp_socket, temp_node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // if we get no node back from port search, we didn't find it and are free to add a new node.
|
||||
temp_node = UDPTreeNodeParamAdd(port, udp_globals.bound_socket_tree); // add new node with port, return its *.
|
||||
UDPTreeNodeQueueAdd(udp_socket, temp_node);
|
||||
}
|
||||
}
|
||||
else // if no bound sockets, we init the tree as a new node
|
||||
{
|
||||
udp_globals.bound_socket_tree = UDPTreeNodeParamInit(port); //... shouuuld be in L.E .. ?
|
||||
UDPTreeNodeQueueAdd(udp_socket, udp_globals.bound_socket_tree); // add the udp socket to the port queue
|
||||
// maybe more checks to do before this, dunno rn.
|
||||
}
|
||||
|
||||
udp_socket->bound_to = port;
|
||||
|
||||
SocketBind(udp_socket->socket); // Advance Socket state-machine to BIND REQ state.
|
||||
|
||||
switch (udp_socket->socket->state)
|
||||
{
|
||||
case SOCKET_STATE_BIND_REQ: // if BIND request success, set BOUND.
|
||||
udp_socket->socket->state = SOCKET_STATE_BOUND;
|
||||
break;
|
||||
|
||||
default:
|
||||
ZenithErr("Unsuccessful UDP Socket Bind: Misconfigured Socket state-machine.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
I64 UDPSocketClose(CUDPSocket *udp_socket)
|
||||
{ // close, pop, and free the socket from the bound tree.
|
||||
CUDPTreeNode *node;
|
||||
CUDPTreeQueue *queue;
|
||||
|
||||
node = UDPTreeNodeFind(udp_socket->bound_to, udp_globals.bound_socket_tree);
|
||||
|
||||
if (node)
|
||||
queue = UDPTreeNodeQueueSocketFind(udp_socket, node);
|
||||
else
|
||||
{
|
||||
Debug("Didn't find node at socket during UDPSocketClose!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (queue)
|
||||
{
|
||||
if (queue == queue->next == queue->last)
|
||||
{ // queue is alone. Means port only has this address bound.
|
||||
Debug("queue==next==last");
|
||||
}
|
||||
else
|
||||
{ // queue has other addresses bound at this port. only Free the queue entry, after removing it.
|
||||
// QueueRemove(queue); // QueueRemove acted strange and did not move the links?.....
|
||||
// UDPTreeNodeQueueSocketSinglePop(udp_socket, node); /// ???? wtf is going on haha
|
||||
|
||||
// Free(udp_socket->socket);
|
||||
// Free(udp_socket);// is it even possible to free a param?
|
||||
// Free(queue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Didn't find queue at socket during UDPSocketClose!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// UDPSocketConnect (Shrine just has FIXME: 'implement')
|
||||
|
||||
// UDPSocketReceiveFrom
|
||||
|
||||
// UDPSocketSendTo
|
||||
|
||||
// UDPSocketSetOpt ?
|
||||
|
||||
// UDPHandle
|
||||
|
||||
// so i guess the socket functions would just act on the socket state machine.
|
||||
// ZenithErr and return fail vals if socket FSM improperly used.
|
||||
// if we're using a global for the bit map and socket pointer map, be careful
|
||||
// with how Free is done.
|
||||
|
||||
UDPGlobalsInit;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Binary file not shown.
@ -1,10 +0,0 @@
|
||||
$TR,"Zenith"$
|
||||
$ID,2$$TR,"SysMessageFlags"$
|
||||
$ID,2$sys_message_flags[0]=0;
|
||||
$ID,-2$$TR,"SysRegVer"$
|
||||
$ID,2$registry_version=1.000;
|
||||
$ID,-2$$ID,-2$$TR,"Once"$
|
||||
$ID,2$$TR,"Zenith"$
|
||||
$ID,2$$ID,-2$$TR,"User"$
|
||||
$ID,2$$ID,-2$$ID,-2$$TR,"AutoComplete"$
|
||||
$ID,2$ac.col=98;ac.row=23;$ID,-2$
|
@ -227,9 +227,11 @@ U0 WinScrollsInit(CTask *task)
|
||||
TaskDerivedValsUpdate(task);
|
||||
}
|
||||
#define VIEWANGLES_SPACING 22
|
||||
#define VIEWANGLES_RANGE 48
|
||||
//#define VIEWANGLES_RANGE 48
|
||||
#define VIEWANGLES_RANGE 360
|
||||
#define VIEWANGLES_BORDER 2
|
||||
#define VIEWANGLES_SNAP 2
|
||||
//#define VIEWANGLES_SNAP 2
|
||||
#define VIEWANGLES_SNAP 1
|
||||
|
||||
U0 DrawViewAnglesCtrl(CDC *dc,CCtrl *c)
|
||||
{
|
||||
|
@ -160,3 +160,29 @@ public U0 PaletteSetNight(Bool persistent=TRUE)
|
||||
fp_set_std_palette = &PaletteSetNight;
|
||||
}
|
||||
//********************************************************************************
|
||||
public CBGR24 gr_palette_tom[COLORS_NUM]= {
|
||||
0xC9C9C9, 0x3465A4, 0x4E9A06, 0x06989A, 0xA24444, 0x75507B, 0xCE982F, 0xD3D7CF,
|
||||
0x555753, 0x729FCF, 0x82BC49, 0x34E2E2, 0xAC3535, 0xAD7FA8, 0xFCE94F, 0x000000
|
||||
};
|
||||
|
||||
public U0 PaletteSetTom(Bool persistent=TRUE)
|
||||
{//Activate tom's palette.
|
||||
GrPaletteSet(gr_palette_tom);
|
||||
LFBFlush;
|
||||
if (persistent)
|
||||
fp_set_std_palette = &PaletteSetTom;
|
||||
}
|
||||
//********************************************************************************
|
||||
public CBGR24 gr_palette_tom_light[COLORS_NUM]= {
|
||||
0x000000, 0x3465A4, 0x4E9A06, 0x06989A, 0xA24444, 0x75507B, 0xCE982F, 0xD3D7CF,
|
||||
0x555753, 0x729FCF, 0x82BC49, 0x34E2E2, 0xAC3535, 0xAD7FA8, 0xFCF29E, 0xC9C9C9
|
||||
};
|
||||
|
||||
public U0 PaletteSetTomLight(Bool persistent=TRUE)
|
||||
{//Activate tom's light palette.
|
||||
GrPaletteSet(gr_palette_tom_light);
|
||||
LFBFlush;
|
||||
if (persistent)
|
||||
fp_set_std_palette = &PaletteSetTomLight;
|
||||
}
|
||||
//********************************************************************************
|
Loading…
x
Reference in New Issue
Block a user