Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!utgpu!water!watmath!clyde!rutgers!ames!ucbcad!ucbvax!GNOME.CS.CMU.EDU!Aaron.Wohl From: Aaron.Wohl@GNOME.CS.CMU.EDU.UUCP Newsgroups: comp.protocols.appletalk Subject: networking comments Message-ID: <1987.4.15.16.42.26.Aaron.Wohl@gnome.cs.cmu.edu> Date: Wed, 15-Apr-87 11:44:12 EST Article-I.D.: gnome.1987.4.15.16.42.26.Aaron.Wohl Posted: Wed Apr 15 11:44:12 1987 Date-Received: Wed, 22-Apr-87 23:35:45 EST Sender: daemon@ucbvax.BERKELEY.EDU Distribution: world Organization: The ARPA Internet Lines: 298 Some questions and answers on network programming on a Macintosh: How does a Desk Accessory (DA) use AppleTalk for asyncronous calls? For example, looking in "Inside Macintosh" (IM) at DDPWrite (Vol.II, pg. 283) and asyncronous calls (Vol.II, pg. 273), you see that when an async call finishes a network event is generated. However, there isn't any way for a DA to get network events, and you can't just let the host program get them, because there isn't any way for the host program to tell that the event is ment for a DA. The answer is that the formalisims detailed in the AppleTalk assembly language section are different from those [NotInROM] described in the Pascal section. Pascal for DDPWrite takes the abRechandle, locks it and does a asyncronous Control call on the .MPP (AppleTalk) driver. The done routine of this request posts the network event. To use DDPWrite from a DA, you must make up the parm block and do the control call yourself. To do this you must either have your own done routine set some status information, or simply poll the parameter block in your DA periodic routine. In order to allow maximum flexibilty to user programs and to allow vertical tasks to use a network driver, the driver should be able to be loaded into 1) the system heap, 2) the application heap, and 3) the above bufptr. IM gives you information concerning how a device tells the device manager that it has completed a request (jIODONE), but there is no mention of what request to complete. My network driver will allow multiple outstanding read requests, but how do I say which request is completing? How does AppleTalk handle this? Device driver requests must complete in the order of arrival. Read requests are not handled thru the device manager. The AppleTalk device has dispatch tables internally (for ALAP and DDP). An entry is placed in these by AttachPH and OpenSkt. When a packet begins to arrive, execution is depatched to the users entry in the appropriate table by the hardware interrupt handler that gets control on a SCC B channel interrupt. So for an individual device manager call there is no driver request. But what if my driver has multiple speed links (e.g. AppleTalk and ethernet), such that if I queue a write for a low speed link and then two packets to write for a highspeed link? The control interface calls for AppleTalk already exist and haven't changed to handle this on a Mac II (which can have multiple interfaces). Also the device manager still does not handle completions out of order. So, I don't know about his one. Anyone know a hack/undocumented feature to have requests complete out of order? The dispatch mechanisim is fine for handling AppleTalk which has small headers. What about IP/TCP and others with large headers which are inclined to use a queue? Dispatching as AppleTalk does automatically separates the application heap read buffers from the system heap/bufptr buffers. A problem only happens if there is a alap listener for a vertical task living above bufptr and get gets a packet and inputs the packet somewhere in its space. If the application should quit just afterwoods there is no danger of it going away. It isn't known who an IP family packet is for until after alot of data has come in; too much to want to copy to the right place. Schemes for allowing vertical interrupt routines to use IP: 1)Allocate static space (bufptr or sysheap) for all packet buffers. This should be avoided as not flexible enough as it would be necessary to reboot to change it. 2)A mixed strategy in which each IP device user supplys enough buffer space for itself. But since packets can't be separated, user packets may be in a DA's buffers, the DA packets in vertical routines buffers, and the vertical routines packets in user buffers. The only potential problem is vertical packets in user/DA buffers. As with the others, a program won't ask for a packet that it knows has arrived after the program is gone. If we allow an IP port to say that a packet has arrived, then up until the time the packet is released it may not be available when asked for. But what about Switcher? Yes, user programs would have to allow for a packet going away also. Another method: Allowing buffers to go away is sort of a cute hack, but I think it would tend to force buffer copying. If at a higher level you are emulating a user protocol that doesn't allow for packets going away, you would have to make a copy of a bufffer if the user routines asked if there was a packet to be sure to have it. However, since I don't know of any high level network interfaces that let you get a packet without releasing it, you might tell the high level system when there is a packet. If the packet buffer needs to be destroyed before it is asked for, then make up bad packet, bad checksum, bad type etc, since everyone allows packets to be garbled. 3)Allow each IP port to use a different DDP port. The new AppleTalk drivers allow many ddp ports. This would also allow different IP implementations to exist in the same machine. Currently everyone uses a fixed DDP type and DDP socket. What would have to be changed to allow this? 4)Allocate fixed space for all the packet headers (at the IP level) in the system heap or bufptr. However, since by the time the header is in, the port is known, have separate buffers available for packets of different life times. Unfortunetly for IP the destination port doesn't tend to be available until after the header options, which are long and can be of variable length. Buffer fragmentation: Packets tend to come in different sizes. The packets sizes depend on the protocol. For example, for Telnet there are little packets for typein and large packets with one system terminal buffer worth of text. Systems that use a heap for packet storage can spend a lot of time cutting up and reassembling packets into the same size units. On c.cs.cmu.edu for example, the the BSP (byte stream protocol (old ethernet)) code spends more than half its time making packets of (statistically speaking) two sizes. Therefore in order to reduce wasted space and increase efficiency, network drivers should consider accepting/allocating a free pool of packets cut up into fixed sizes. This is especially the case where the size of an incoming packet can be determined ahead of time and copying can be avoided. Even if the size is not known ahead of time, packets tend to come in maximum size and small size. A minimal size data packet sitting in a maximum size buffer might be worth copying to a short buffer. If I assert a NBP name from a program and the program exits the name goes away. If I assert a name from a vertical task it stays around across launches. Why/how? When NBP gets a goodbye kiss it scans all it's name blocks and removes the names of blocks in the application heap. What does the AppleBus variables pointer point to? This varies from version to version of the driver. As with the Mac+ these contain the protocol dispatches, temporaries for interrupt routines, etc (see attached documentation). What if I open AppleTalk (Mac512 not E or +) with heap scrabble on and the system crashes? The .MPP driver in the system file open routine does a set handle size (to free the space used by the AppleTalk initilization routines) from the open routine. This places the block where execution returns to after the SetHandleSize in the free space. So, heap scrabble is the problem, since SetHandleSize is documented to never compact if the block is geting smaller. But still the driver is silly to depend on executing code in the memory managers free list. If I don't call system task, NBP still defends it's name. It does this through a combination of getting dispatches from the DDP protocol table as packets for it arrive, and having a vertical retrace task. The AppleTalk calls are listed in the list of calls that cause garbage collection. How can they be done from a vertical routine? The Pascal glue cannot be called as that interface uses handles. Most of the control calls to the AppleTalk driver can be made from vertical routines. Attach/detach of lap and ddp are known to work fine. Versions of AppleTalk before the shared file system did not allow more than one NBP call to be active at a time (there was a global with the current request number). Why are all the Appletalk calls Control calls on the driver, and not read/write/status? This is because read and write calls are for streams of data, and also it makes for one common entry point. You don't know assembler, and need to make a vertical routine. How do you do the Control() call to .MPP from .C or Pascal? Especially as your test programs just seem to crash. Look at IM Vol. II, pg. 283. csParam is defined as an ARRAY [0..10] OF INTEGER. Now look at IM Vol.lI, pg. 323 under LookupName. It talks of parameter block offsets upto 42. What is the correspondance? AppleTalk misuses the cntrlParam parameter of control calls. It is supposed to be 22 (sizeof(ARRAY [0..10] OF INTEGER)) bytes long. The Control() call (IM Vol.ll, pg. 279) allocates a ParamBlockRec for you, copies the csCode you pass to csCode, and copies 22 bytes from the address you specify from csParamPtr argument. For AppleTalk this is not corrent. You need to be able to look at the results of the call and since the data pointed to by csParamPtr is copied to some internal buffer you cannot find it. Therefore, you need to make up your own ParamBlockRec. csCode of the ParamBlockRec is the csCode at offset 26 of the ParamBlockRec talked about in IM Vol.II, pg. 23. For example, after the csCode, are the rest of the AppleTalk parameters are at the offset that the csParam should be. Here is how the SCC hardware and interrupt routines receive AppleTalk packets. See figure 4 in "Inside Macintosh", pages III-26, to see how the signals from AppleTalk get to SCC (they come in on RXD- and RXD+ from the little box hanging out back). The SCC (Zilog 8530) serial communications controller receives the bits and assembles them into bytes. If you are going to be programming the SCC, you will need to get a copy of the Z8530 section of a Zilog data book, which will explain (to some extent) what the zillions of registers are for. Macintosh to SCC I/O Interface: The SCC has two registers presented to the bus for each channel (A and B). Each register can be read and written to. For writing to the SCC use the address in system global SCCWr + (aData(6), bData(4), aCtl(2), bCtl(0)) depending on which register you want. For reading to the SCC, use SCCRd, which is accessed the same way. To read or write to the SCC, you generally do a write to the control port to tell it which internal register you want. Then you do a read or a write to the control port again to get/send the data you want Be sure to have at least one instruction delay between accesses (one NOP instruction is fine) to meet the SCC timing requirments. Macintosh to SCC Interrupts: There are two channels, SCC B and SCC A, which are basically independent in the operation and seperation of their registers and wiring. The only exceptions are RR2 and RR3 (read registers 2 and 3). RR3 is the interrupt pending status register and can only be read from channel B. RR2 is the interrupt vector register (SCC interrupts do not vector according to this hardware vector, they vector according to 68000 interrupt level only - more on this later). If you read RR2 from the B side it always returns the interrupt vector that the SCC was initilized with (which should always be set to zero or the ROM interrupt handler will be confused). However, when you read from side A the interrupt vector is modified by the 8 different possible pending interrupts (according to the highest SCC priority). When the SCC requests an interrupt from the 68K, the 68K pushes the SR (status /CCR register) and PC onto the stack. Since the SCC is on level 2 (see "Inside Macintosh", pages III-197 fig 4) the 68K loads the contents of the long word at $68 into the program counter. On my Macintosh Plus, $68 points to $401A84, which does the following: save D0-D3/A0-A3; /* preserve the users registers */ A0 := SCCRd; /* setup SCC read/write addresses */ A1 := SCCWr; D0 := *SCCRd & 0xe0; /* see who is interrupting within the SCC */ if(D0>=8) { /* channel A? */ A0 +=2; /* yes, change SCC addresses to point to */ A1 +=2; /* SCC A registers instead of B registers */ } /* else A0/A1 point to B */ A2 := Lvl2DT[D0]; /* select service routine */ JSR (A2) /* call the service routine */ restore D0-D3/A0-A3; /* put the users register back */ RTE /* return from interrupt */ Put your own transmit buffer empty, receive special condition, and receive character interrupt handlers into Lvl2DT. Do *NOT* change the external status entry in Lvl2DT because the mouse vertical and horizotal position sensors are connected to a general purpose input to the SCC. If the mouse moves vertically you get SCC B external interrupts. So do not change the external interrupt in Lvl2DT, as it checks to see if the interrupt is due to the mouse or communications, and vectors thru ExtStsDT. Again on my Macintosh Plus, Lvl2DT+4 (SCC B external) points to this routine in the ROM: channel_B_external_interrupt: D0 :=SCCRd+bCtl; /* get SCC status */ A2 :=ExtStsDt; /* point to the B side of ExtStsDt */ A3 := &SCCBSts; /* point to SCC status byte in low memory */ goto common_external_interrupt channel_A_external_interrupt: D0 :=SCCRd+aCtl; /* get SCC status */ A2 :=ExtStsDt+8; /* point to the A side of ExtStsDt */ A3 := &SCCASts; /* point to SCC status byte in low memory */ common_external_interrupt: /* here we set D1 to be those status bits in RR0 (abort, tx undeflow, CTS (clear to send), sync/hunt, DCD (mouse), Tx empty, zero in baud generator, Rx available, anded with those channels that are enabled. In other words, D1 is those RR0 conditions that are different that we are interested in. */ D1 := ( *SCCxSts Xor RR0) && RR15; /* save current state so next interrupt can know what is different */ *SCCxSts := D0; /* is the *ONLY* new status change due to the mouse? */ if( D1 = 8) /* check DCD SCC input */ A2 +=2; /* yes,just the mouse, select mouse interrupt routine */ /* *************** very important ************** */ /* tell the SCC that the current interrupt had been serviced, (end IUS interrupt under service. NOTE: for all the other routines you might write interrupt handlers for (data interrupt, trasmit, special) they must do a IUS to the SCC. For the external interrupt, the user routine does not, as it is done right here. */ WW0 := $10; /* end interrupt under service */ /* go to user external service routine. registers: D0 - SCC status D1 - SCC status that is different since the last interrupt D2 - trash D3 - trash A0 - SCC read address A1 - SCC write address A2 - trash A3 - trash You must preserve all the other regs. D0-D3/A0-A3 may be trashed. N.B. SCC communication conditions have priority over mouse, so if there is a communications status change at the same time the mouse moves then this JMP (A2) only calls the communication routine. Some communication routines have been seen to look at D1 and notice that there is a mouse interrupt also and jump to mouse routine when done servicing the communication interrupt. This is fine, but in that case preserve A0,A1, D0,D1 as the mouse routine needs them. */ JMP (A2) *****************************************************************************