BBC IP Networking API --------------------- File: SocketRFC - Update: 0.11 Author: J.G.Harston - Date: 18-Dec-2010 Updated Sockets networking API. This initially started as a discussion on the BBC Micro Mailing List[1]. This has become the finally implemented API. Incorporating thse comments: * Doesn't have to be specifically Ethernet, can be any IP network * Discussion on socket number being an opaque 8-bit number * Internet events * Resolver API * NFS-style API Initial Notes ------------- * Doesn't have to be specifically Ethernet, can be any IP network. Renamed API as IP Networking API. * Socket number can be an opaque 8-bit number. The initial proposal included a 32-bit socket number by virtue of it being at X%!4 in the OSWORD control block. It was pointed out that a 32-bit socket number implies 4 billion sockets can be open at any time. Even if the socket number was actually an address of a socket block, it would be memory in the IP networking workspace, so didn't need to be a full 32-bit number. An 8-bit number, as with NFS receive blocks, is adequate for 255 open sockets, and an 8-bit number more easily fits into the control block at X%?3, filling in what was previously listed as "unused". Also, an 8-bit number makes implementing Events easier, see next. However, the actual implementaion used 32-bit socket numbers at X%!4 in the control block as in the initial proposals. IP Networking API - OSWORD 192 (&C0) ------------------------------------ On entry: A%=192, X%=control block, Y%=X%DIV256 X%?0 = send block length X%?1 = receive block length X%?2 = command, 0-63=Socket, 64-127=Resolver, 128-255=Reserved X%?3 = 0, returns 0 or error value X%!4 = socket X%!8 =>data X%!12 = data length X%!16 = flags X%!20 =>IP address X%!24 = IP address length, if zero, X%!20 = IPv4 address On exit: X%?2 = 0 if call supported, unchanged if call unsupported or no handler present X%?3 = 0 or returned error X%!4 onwards: any returned data Memory pointed to by X%!8 on entry may be modified Commands passed in X%?2 are those in the RISC OS Socket_ SWIs, that is: &00 Socket_Creat X%!4=domain, X%!8=type, X%!12=protocol &01 Socket_Bind X%!4=sock, X%!8=name, X%!12=namelen &02 Socket_Listen X%!4=sock, X%!8=backlog &03 Socket_Accept X%!4=sock, X%!8=addr, X%!12=len &04 Socket_Connect X%!4=sock, X%!8=name, X%!12=namelen &05 Socket_Recv X%!4=sock, X%!8=buf, X%!12=len, X%!16=flags &06 Socket_Recvfrom X%!4=sock, X%!8=buf, X%!12=len, X%!16=flags, X%!16=from, X%!20=fromlen &07 Socket_Recvmsg X%!4=sock, X%!8=msg, X%!12=flags &08 Socket_Send X%!4=sock, X%!8=msg, X%!12=len, X%!16=flags &09 Socket_Sendto X%!4=sock, X%!8=msg, X%!12=len, X%!16=flags, X%!16=to, X%!20=tolen &0A Socket_Sendmsg X%!4=sock, X%!8=msg, X%!12=flags &0B Socket_Shutdown X%!4=sock, X%!8=how &0C Socket_Setsockopt X%!4=sock, X%!8=level, X%!12=optname, X%!16=optval, X%!16=optlen &0D Socket_Getsockopt X%!4=sock, X%!8=level, X%!12=optname, X%!16=optval, X%!16=optlen &0E Socket_Getpeername X%!4=sock, X%!8=name, X%!12=namelen &0F Socket_Getsockname X%!4=sock, X%!8=name, X%!12=namelen &10 Socket_Close X%!4=sock &11 Socket_Select X%!4=nfds, X%!8=readfds, X%!12=writefds, X%!16=exceptfds, X%!16=timeout &12 Socket_Ioctl X%!4=sock, X%!8=request, X%!12=argp &13 Socket_Read X%!4=sock, X%!8=buf, X%!12=num &14 Socket_Write X%!4=sock, X%!8=buf, X%!12=num &15 Socket_Stat X%!4=sock, X%!8=buf &16 Socket_Readv X%!4=sock, X%!8=iov, X%!12=iovcnt &17 Socket_Writev X%!4=sock, X%!8=iov, X%!12=iovcnt The following is a BASIC Sockets library for the calls most useful to be implemented: REM > BLib.Socket DEFFNSocket_Open( X%!4, X%!8, X%!12):A%=0 DEFFNSocket_Bind( X%!4, X%!8, X%!12):A%=1 DEFFNSocket_Listen( X%!4, X%!8) :A%=2 DEFFNSocket_Accept( X%!4, X%!8, X%!12):A%=3 DEFFNSocket_Connect(X%!4, X%!8, X%!12):A%=4 DEFFNSocket_Read( X%!4, X%!8, X%!12):A%=5 DEFFNSocket_Write( X%!4, X%!8, X%!12):A%=8 DEFFNSocket_Close( X%!4) :A%=16 !X%=&814:X%?2=A%:A%=192:CALL &FFF1:IFX%?3:=-X%?3 ELSE =X%!4 The calls return a negative number if the call fails, or a positive number (or zero) if the call succeeds, being the returned socket number or byte count, etc. Resolver API - OSWORD 192 (&C0) ------------------------------- On entry: A%=192, X%=control block, Y%=X%DIV256 X%?0 = send block length X%?1 = receive block length X%?2 = command, 0-63=Socket, 64-127=Resolver, 128-255=Reserved X%?3 = 0, returns 0 or error value X%!4 =>host name On exit: X%?2 = 0 if call supported, unchanged if call unsupported or no handler present X%?3 = 0 or returned error X%!4 onwards: any returned data Memory pointed to by X%!8 on entry may be modified Commands passed in X%?2 are those in the RISC OS Resolver_ SWIs plus &40, that is: &40 Resolver_GetHostByName On entry: X%!4=>name, On exit: X%!4=>name, X%!8=>aliases, X%!12=>type, X%!16=length, X%!20=>IP addresses &41 Resolver_GetHost On entry: X%!4=>name, On exit: X%!4=>name, X%!8=>aliases, X%!12=>type, X%!16=length, X%!20=>IP addresses The following is a BASIC Resolver library: REM > BLib.DNS DEFFNDNS_GetHostByName(A$):A%=64 DEFFNDNS_GetHost( A$):A%=65 !X%=&1808:X%?2=A%:X%!4=name%:$name%=A$:A%=192:CALL&FFF1:=X%+4 AND X%?3=0 The calls return zero if the call fails, or the address of an inet address structure. NFS-style API ------------- To do a full IP transaction just using sockets requires several calls and checks, eg to transmit a datagram you have to open a socket, bind it, write to it and then close it, whereas the NFS API gives you eg Net_Tx(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%) in one go. Also, to allow patching of Econet to let NetFS (ie BBC NFS) pass calls to IP, there needs to be a call that 'looks like' the control blocks that NFS passes on to Econet. With appropriate programming (eg using the Net library at mdfs.net/blib) station numbers (network addresses) are already held as 32-bit numbers, so is transparently transfered to IPv4 netwworking by just rebuilding code with a new library. For example, A%=FNNet_RxRead(rxnum%):Stn%=X%!3 sets Stn% to &0000 and program code will transparently deal with what's effectively 0.0.net.stn just as well as one.two.three.four. Effectively, a 2-byte Econet address is embedded in a 4-byte IPv4 address in the same way that a 4-byte IPv4 address is embedded in a 16-byte IPv6 address. In fact, checking through my TALK (netchat) and MUGINS (multi-user network game) I can't see anything that would stop them working over IP. Both work correctly on A5000s with Ethernet interfaces. IP Events --------- Event number 19, X=subcode, Y=socket number. As with ANFS receive events, will need its own enable/disable code, as the MOS *FX13/14 only deals with events 0-9. Event parameters on an 8-bit platform being inherently 8-bit means that socket numbers are actually 8-bit numbers, they just happen to be manipulated as 32-bit words in the OSWORD control block. Needs a suitable call to enable/disable IP events. By default, events are disabled. Could start at the top of the IP subcall numbers, and use subcall number 63 downwards, unless something like the Socket_SetOpt call would be more appropiate. A lot of the documentation I have refers to defined values (eg SO_REUSEADDR), and without checking a suitable header file, I'm not sure of their exact values. When enabled, the following events occur: A=&13, X=1, Y=socket Socket Y has input waiting to be read A=&13, X=2, Y=socket Socket Y has out-of-band data arrived A=&13, X=3, Y=socket Socket Y's connection has been broken Econet hooks to IP ------------------ On entry: OSWORD A%=&18, X%=control block, Y%=X%DIV256 Control block as for OSWORD &10, Network Transmit On entry: OSWORD A%=&19, X%=control block, Y%=X%DIV256 Control block as for OSWORD &11, Network Receive OSWORD numbers chosen to be the NFS/Econet numbers + &08. These calls are passed on from a patched NFS to transmit to or listen from reception from IP via AUN UDP datagrams. The handler should update the control block, which will be in Econet/NFS's workspace, and so is polled by the usual Net_TxPoll, Net_RxPoll OSBYTEs. This needs a method to map the Econet net numbers to appropriate IP addresses. AUN specified net &80+ as mapping to an IP net, eg Econet 128.xxx maps to IP aaa.bbb.ccc.xxx. It will need suitable defaults (maybe in CMOS RAM?) and commands to set/change/read them. eg *AUNMap 128 192.168.10 *AUNMap 128.254 192.168.10.2 *Configure AUNMap 128 192.168.10 ...? Econet-style IP transmit/receive calls -------------------------------------- DEFFNIP_Tx(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%) DEFFNIP_TxCount(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%,Try%,Delay%) DEFFNIP_RxOpen(Stn%,Port%,Addr%,Len%) DEFFNIP_Rx(RxNum%) DEFFNIP_RxRead(RxNum%) DEFPROCIP_RxKill(RxNum%) See Also -------- * URI Dispatch API, mdfs.net/Docs/Comp/BBC/URIFetcher * Sprow Ethernet Interface, sprow.co.uk/bbc/masternet.htm * Mailing List Discussion, mdfs.net/Archive/BBCMicro/2009/07/23/015012.htm