! LANCE driver for interim Mouse, GDMR, Jan 1988. %option "-NonStandard-Low-NoCheck-NoTrace-NoDiag" %constinteger RX buffer size = 1600 %constinteger RX buffers = 48 %constinteger max inbound slot = 8 %include "Moose:Mouse.Inc" %include "GDMR_E:Lance.Inc" %include "GDMR_E:Ether.Inc" %include "GDMR_E:L_Cntrl.Inc" %systemintegerfnspec free store %systemintegerfnspec global heap get(%integer size) %systemroutinespec phex(%integer i) %systemroutinespec phex2(%integer i) %systemroutinespec phex4(%integer i) %externalroutinespec FS insert(%string(31) what, %integer label) %externalpredicatespec FS lookup(%string(31) what, %integername value) !! %include "GDMR_H:Dump.Inc" %owninteger control = 0 %constinteger RX size = 10240, RX priority = 7 %constinteger TX size = 10240, TX priority = 7 %constinteger error size = 10240, error priority = 7 %constinteger lance IPL = 3 @16_00FE0000 %writeonly %volatile %half lance address, lance data %constinteger C err bit = 15 %constinteger C babl bit = 14 %constinteger C cerr bit = 13 %constinteger C miss bit = 12 %constinteger C merr bit = 11 %constinteger C rint bit = 10 %constinteger C tint bit = 9 %constinteger C idon bit = 8 %constinteger C intr bit = 7 %constinteger C inea bit = 6 %constinteger C rxon bit = 5 %constinteger C txon bit = 4 %constinteger C tdmd bit = 3 %constinteger C stop bit = 2 %constinteger C strt bit = 1 %constinteger C init bit = 0 %constinteger clear interrupt = C babl ! C miss ! C merr ! %c C rint ! C tint ! C idon %recordformat ether address fm(%bytearray x(0 : 5)) %recordformat ether header fm(%record(ether address fm) destination, %record(ether address fm) source, %short type) %ownrecord(ether address fm) our ether address = 0 %ownrecord(semaphore fm) RX semaphore = 0 %ownrecord(semaphore fm) TX semaphore = 0 %ownrecord(semaphore fm) error semaphore = 0 %owninteger babl tally = 0 %owninteger miss tally = 0 %owninteger merr tally = 0 %owninteger fram tally = 0 %owninteger oflo tally = 0 %owninteger CRC tally = 0 %owninteger buff tally = 0 %owninteger more tally = 0 %owninteger one tally = 0 %owninteger def tally = 0 %owninteger uflo tally = 0 %owninteger lcol tally = 0 %owninteger lcar tally = 0 %owninteger rtry tally = 0 %owninteger no takers tally = 0 %owninteger packets in = 0 %owninteger bytes in = 0 %owninteger packets out = 0 %owninteger bytes out = 0 %ownrecord(ibf) ib = 0 %ownrecord(drf)%name RX ring == nil %ownrecord(drf)%name TX ring == nil ! Initialisation and interrrupt handler %routine initialise lance %ownrecord(interrupt handler fm) handler = 0 %ownrecord(semaphore fm) initialisation semaphore = 0 %label H, TX, babl, miss, merr, idon, done ! Turn off the chip before we do anything else ! (just to be safe...) lance address = 0; lance data = c stop ! Now set up data structures setup semaphore(initialisation semaphore) setup interrupt handler(handler, addr(H)) add interrupt handler(handler, lance IPL) ! Initialise the descriptor rings and the board here, then ! start it all going. Note that we ASSUME that once the chip is ! initialised we will only be accessing lance register CSR0, and ! hence we won't need to keep selecting it..... RX ring == record((global heap get(size of(RX ring) + 8) + 7) & (\ 7)) TX ring == record((global heap get(size of(TX ring) + 8) + 7) & (\ 7)) RX ring = 0; TX ring = 0 FS insert("LANCE_TX_RING", addr(TX ring)) FS insert("LANCE_RX_RING", addr(RX ring)) !! printstring("RX ring at "); phex(addr(RX ring)) !! printstring(", TX ring at "); phex(addr(TX ring)); newline ib = 0; ! Clear extraneous stuff ib_mode = 0; ! Enable TX, RX, CRC ib_pa0 = our ether address_x(1) << 8 ! our ether address_x(0) ib_pa2 = our ether address_x(3) << 8 ! our ether address_x(2) ib_pa4 = our ether address_x(5) << 8 ! our ether address_x(4) ib_lowrad <- addr(RX ring) & 16_FFFF ib_highrad <- (addr(RX ring) >> 16) & 255 ib_rcount = 16_E0; ! 128 buffers ib_lowtad <- addr(TX ring) & 16_FFFF ib_hightad <- (addr(TX ring) >> 16) & 255 ib_tcount = 16_E0; ! 128 buffers lance address = 1; lance data <- addr(ib) & 16_FFFF lance address = 2; lance data <- (addr(ib) >> 16) & 255 lance address = 3; lance data = bswp lance address = 0; lance data = c init ! c strt ! c inea semaphore wait(initialisation semaphore) !! printstring("LANCE initialised, CSR0 "); phex4(lance data); newline %return H: ! Interrupt handler. CSR0 assumed selected. We read the CSR ! once only -- perhaps we should reread it for each section? *move.w lance data, D1 { Get CSR0 *btst.w #C intr bit, D1 { Did we interrupt? *beq done { No, -> !! put long(D1); put sym(13); put sym(10) *btst.w #C rint bit, D1 { RX interrupt? *beq TX { No, -> int signal semaphore(RX semaphore) { Kick RX process lance data = C rint ! C inea { Clear RX interrupt -> H { Round for another one TX: *btst.w #C tint bit, D1 { TX interrupt? *beq babl { No, -> int signal semaphore(TX semaphore) { Kick TX process lance data = C tint ! C inea { Clear TX interrupt -> H { Round for another one babl: *btst.w #C babl bit, D1 { Babbling? *beq miss { No, -> *addq.l #1, babl tally { Note error ! int signal semaphore(error semaphore) { Kick error process lance data = C babl ! C inea { Clear babble bit -> H { Round for another one miss: *btst.w #C miss bit, D1 { Missed packet? *beq merr { No, -> *addq.l #1, miss tally { Note error ! int signal semaphore(error semaphore) { Kick error process lance data = C miss ! C inea { Clear miss bit -> H { Round for another one merr: *btst.w #C merr bit, D1 { Memory error? *beq idon { No, -> *addq.l #1, merr tally { Note error ! int signal semaphore(error semaphore) { Kick error process lance data = C merr ! C inea { Clear memory error (but the chip { has stopped...) -> H { Round for another one idon: *btst.w #C idon bit, D1 { Initialisation done? *beq done { No, no more to check, -> int signal semaphore(initialisation semaphore) lance data = C idon ! C inea { Clear init done -> H { Round for another one done: return from interrupt *move.l D0, D0 %end ! Receiver buffer management %recordformat RX buffer fm(%bytearray data(1 : RX buffer size), %integer bytes, %record(RX buffer fm)%name next) %constinteger RX total size = RX buffer size + 8 ! Extra fields at tail rather than head so that buffer address can be ! extracted directly from the descriptor ring entry. %ownrecord(semaphore fm) RX add semaphore = 0 %routine add RX to ring(%record(RX buffer fm)%name b) %owninteger next add position = 0 %record(df)%name entry !! printstring("Add RX buffer at "); phex(addr(b)) !! printstring(" to slot "); write(next add position, 0); newline semaphore wait(RX add semaphore) entry == RX ring_desc(next add position) %if entry_control & R own # 0 %start ! Owned by the chip. Disaster printstring("Lance RX slot "); write(next add position, 0) printstring(" owned by chip"); newline %stop %finish entry_lowad <- addr(b) & 16_FFFF entry_highad <- (addr(b) >> 16) & 255 entry_minus = -RX buffer size entry_plus = 0 entry_control = R own; ! Give it to the chip next add position = (next add position + 1) & 127 signal semaphore(RX add semaphore) %end %recordformat inbound slot fm(%integer type, %record(RX buffer fm)%name buffers, %record(ether request fm)%name requests) %ownrecord(inbound slot fm)%array inbound slots(1 : max inbound slot) = 0(*) %ownrecord(semaphore fm) inbound semaphore = 0 %routine show ether address(%record(ether address fm)%name a) %integer i phex2(a_x(i)) %and space %for i = 0, 1, 5 %end %routine copy inbound(%record(RX buffer fm)%name b, %record(ether request fm)%name r) %integer from, to, bytes %label CL from = addr(b_data(1)) to = addr(r_buffer) bytes = b_bytes - 1 !! printstring("Copy inbound: "); write(bytes + 1, 0) !! printstring(" from "); phex(from) !! printstring(" to "); phex(to); newline *move.l from, A0 *move.l to, A1 *move.L bytes, D0 CL:*move.b (A0)+, (A1)+ *dbra D0, CL r_bytes = b_bytes %end %routine process inbound(%record(RX buffer fm)%name b) %record(RX buffer fm)%name lb %record(ether header fm)%name h %record(inbound slot fm)%name in, all == nil %record(ether request fm)%name r %integer i packets in = packets in + 1 bytes in = bytes in + b_bytes h == record(addr(b_data(1))) !! printstring("To: "); show ether address(h_destination); newline !! printstring("From: "); show ether address(h_source); newline !! printstring("Type: "); phex4(h_type); newline !! dump(b_bytes - 14, b_data(15)) semaphore wait(inbound semaphore) %for i = 1, 1, max inbound slot %cycle in == inbound slots(i) %if in_type = h_type %or (0 < h_type <= 1500 %and in_type = 1) %start ! Match. !! printstring("Match"); newline matching: %if in_requests ## nil %start ! Read request pending. Unlink it, copy in our data ! and send it back to the requestor. !! printstring("Pending request"); newline r == in_requests in_requests == r_next signal semaphore(inbound semaphore) copy inbound(b, r) r_status = ether success send message(r, r_system part_reply, nil) %c %if r_system part_reply ## nil ! Now return the buffer to the pool add RX to ring(b) %else ! No request pending. Enqueue our buffer on the end of ! the pending buffer list. !! printstring("Link us on"); newline b_next == nil %if in_buffers == nil %start in_buffers == b %else lb == in_buffers lb == lb_next %while lb_next ## nil lb_next == b %finish signal semaphore(inbound semaphore) %finish %return %else %if in_type < 0 ! Catch-all type all == in %finish %repeat in == all %and -> matching %if all ## nil; ! Catch-all signal semaphore(inbound semaphore) %if control & trace no takers # 0 %start show ether address(h_source); printstring(" -> ") show ether address(h_destination); printstring(" type ") phex4(h_type); printstring(" dropped"); newline %finish no takers tally = no takers tally + 1 add RX to ring(b); ! Free it up again %end ! Transmit data structures. Parallel to the TX ring we keep a ring of ! request pointers: when we add a request to the TX ring we note its ! corresponding message in the request ring for later use by the interrupt ! handler. %ownrecord(ether request fm)%namearray TX requests(0 : 127) == nil(*) %routine add TX request(%record(ether request fm)%name r) %owninteger next add position = 0 %record(df)%name entry !! printstring("Add TX request: "); write(r_bytes, 0); newline !! dump(r_buffer, r_bytes) entry == TX ring_desc(next add position) %if entry_control & T own # 0 %start ! Slot owned by device. All we can do is bounce the ! user's request. !! printstring("Not owned, bounce it"); newline r_status = ether no free slot send message(r, r_system part_reply, nil) %c %if r_system part_reply ## nil %return %finish ! Slot is OK. Note the request, then set up the TX entry. packets out = packets out + 1 bytes out = bytes out + r_bytes TX requests(next add position) == r entry_lowad <- addr(r_buffer) & 16_FFFF entry_highad <- (addr(r_buffer) >> 16) & 255 entry_minus = -r_bytes entry_plus = 0 ! Now kick the chip !! printstring("About to kick the chip"); newline entry_control = T own ! T stp ! T enp {TD} lance data = C tdmd ! C inea ! On to next slot next add position = (next add position + 1) & 127 %end ! Interrupt handling processes %routine RX process %owninteger next remove position = 0 %record(RX buffer fm)%name b %record(df)%name entry %integer x open input(2, ":N"); select input(2) open output(2, ":T"); select output(2) %cycle semaphore wait(RX semaphore) !! printstring("RX semaphore signalled"); newline %cycle !! printstring("Remove RX entry at ") !! write(next remove position, 0); newline entry == RX ring_desc(next remove position) %if entry_control & R own = 0 %and entry_minus # 0 %start ! We own it and haven't already processed it. ! Get the address, check the status x = entry_highad << 16 ! entry_lowad !! printstring("Buffer at "); phex(x); newline b == record(x) !! printstring("Flags "); phex2(entry_control); newline %if entry_control & R err = 0 %start ! Packet is OK. Note its size and process it. ! The book says the size is in BCD (p.26), but this ! appears to be fictitious.... b_bytes = entry_plus process inbound(b) %else ! Error in this one. Tally it, then put the ! buffer back on the receive ring. fram tally = fram tally + 1 %if entry_control & R fram # 0 oflo tally = oflo tally + 1 %if entry_control & R oflo # 0 CRC tally = CRC tally + 1 %if entry_control & R CRC # 0 buff tally = buff tally + 1 %if entry_control & R buff # 0 !! printstring("Lance: dropping "); phex2(entry_control) !! printstring(" FRAM") %if entry_control & R fram # 0 !! printstring(" OFLO") %if entry_control & R oflo # 0 !! printstring(" CRC" ) %if entry_control & R CRC # 0 !! printstring(" BUFF") %if entry_control & R buff # 0 !! newline add RX to ring(b) signal semaphore(error semaphore) %finish entry_minus = 0; ! So we know it's been seen.... next remove position = (next remove position + 1) & 127 %else ! Owned by chip or already processed, back to sleep %exit %finish %repeat %repeat %end %routine TX process %owninteger next remove position = 0 %record(ether request fm)%name r %record(df)%name entry open input(2, ":N"); select input(2) open output(2, ":T"); select output(2) %cycle semaphore wait(TX semaphore) !! printstring("TX semaphore signalled"); newline %cycle entry == TX ring_desc(next remove position) !! phex2(entry_control); space; phex4(entry_plus); space !! phex2(entry_highad); phex4(entry_lowad); space !! write(entry_minus, 0); newline %if entry_control & T own = 0 %and entry_control & T stp # 0 %start ! We own it, start of packet. Check the status, tell the user. ! Beware spurious signals -- we assume that if stp isn't ! set then the apparently-completing request is bogus. r == TX requests(next remove position) TX requests(next remove position) == nil %if entry_control & T err = 0 %start r_status = ether success %if r ## nil %else r_status = ether TX failed %if r ## nil buff tally = buff tally + 1 %if entry_plus & D buff # 0 uflo tally = uflo tally + 1 %if entry_plus & D uflo # 0 lcol tally = lcol tally + 1 %if entry_plus & D lcol # 0 lcar tally = lcar tally + 1 %if entry_plus & D lcar # 0 rtry tally = rtry tally + 1 %if entry_plus & D rtry # 0 !! printstring("Lance: TX "); phex2(entry_plus) !! printstring(" BUFF") %if entry_plus & D buff # 0 !! printstring(" UFLO") %if entry_plus & D uflo # 0 !! printstring(" LCOL") %if entry_plus & D lcol # 0 !! printstring(" LCAR") %if entry_plus & D lcar # 0 !! printstring(" RTRY") %if entry_plus & D rtry # 0 !! newline %finish send message(r, r_system part_reply, nil) %c %if r ## nil %and r_system part_reply ## nil more tally = more tally + 1 %if entry_control & T more # 0 one tally = one tally + 1 %if entry_control & T one # 0 def tally = def tally + 1 %if entry_control & T def # 0 entry_control = 0; ! Mark it requestless next remove position = (next remove position + 1) & 127 %else ! Owned by the chip or bogus, back to sleep %exit %finish %repeat %repeat %end !%routine error process ! %owninteger last babl tally = 0 ! %owninteger last miss tally = 0 ! %owninteger last merr tally = 0 ! %owninteger last fram tally = 0 ! %owninteger last oflo tally = 0 ! %owninteger last CRC tally = 0 ! %owninteger last buff tally = 0 ! open input(2, ":N"); select input(2) ! open output(2, ":T"); select output(2) ! %cycle ! semaphore wait(error semaphore) ! printstring("Lance: BABL ") %if babl tally # last babl tally ! printstring("Lance: miss ") %if miss tally # last miss tally ! printstring("Lance: MErr ") %if merr tally # last merr tally ! %if babl tally # last babl tally %or miss tally # last miss tally %c ! %or merr tally # last merr tally %or fram tally # last fram tally %c ! %or oflo tally # last oflo tally %or CRC tally # last CRC tally %c ! %or buff tally # last buff tally %start ! printstring("Lance: ") ! write(packets in, 0); printstring(" in, ") ! write(babl tally, 0); printstring(" babl, ") ! write(miss tally, 0); printstring(" miss, ") ! write(merr tally, 0); printstring(" merr, ") ! write(fram tally, 0); printstring(" fram, ") ! write(oflo tally, 0); printstring(" oflo, ") ! write(CRC tally, 0); printstring(" CRC, ") ! write(buff tally, 0); printstring(" buff") ! newline ! last babl tally = babl tally; last miss tally = miss tally ! last merr tally = merr tally; last fram tally = fram tally ! last oflo tally = oflo tally; last CRC tally = CRC tally ! last buff tally = buff tally ! %finish ! %repeat !%end ! User request interpreter %routine interpret request(%record(ether request fm)%name r) %record(inbound slot fm)%name in %record(RX buffer fm)%name b %record(ether request fm)%name lr %record(ether header fm)%name h %record(ether address fm)%name a %record(ether statistics fm)%name stats %integer i %if r_code = ether read %start !! printstring("Ether read"); newline %unless 0 < r_slot <= max inbound slot %start r_status = ether undefined -> send %finish in == inbound slots(r_slot) %if in_type = 0 %start r_status = ether undefined -> send %finish semaphore wait(inbound semaphore) %if in_buffers == nil %start ! Nothing pending, we'll have to queue. !! printstring("Queueing"); newline r_next == nil %if in_requests == nil %start in_requests == r %else lr == in_requests lr == lr_next %while lr_next ## nil lr_next == r %finish signal semaphore(inbound semaphore) %return %else ! There's a buffer pending. Dequeue it and copy its data ! into our buffer. !! printstring("Buffer pending"); newline b == in_buffers in_buffers == b_next signal semaphore(inbound semaphore) copy inbound(b, r) add RX to ring(b); ! Give buffer back to chip r_code = ether success -> send %finish %else %if r_code = ether write !! printstring("Ether write "); newline %unless 0 < r_slot <= max inbound slot %and 60 <= r_bytes <= 1500 %start r_status = ether undefined -> send %finish in == inbound slots(r_slot) %if in_type <= 0 %start; ! NB: don't allow catch-all to send r_status = ether undefined -> send %finish h == record(addr(r_buffer)) h_source = our ether address h_type = in_type %if in_type > 1500; ! Not ISO add TX request(r) %return %else %if r_code = ether register %and %c (r_type = 1 {ISO} %or r_type = -1 {all} %or 1500 < r_type <= 16_7FFF) !! printstring("Register "); phex4(r_type); newline semaphore wait(inbound semaphore) %for i = 1, 1, max inbound slot %cycle in == inbound slots(i) %if in_type = 0 %start ! This one's free !! printstring("Slot "); write(i, 0); newline in_type = r_type signal semaphore(inbound semaphore) r_slot = i r_status = ether success -> send %finish %repeat signal semaphore(inbound semaphore) r_status = ether no free slot %else %if r_code = ether unregister r_status = ether undefined %else %if r_code = ether address a == record(addr(r_buffer)) a = our ether address r_bytes = 6 r_status = ether success %else %if r_code = ether statistics stats == record(addr(r_buffer)) stats_address(i) = our ether address_x(i) %for i = 0, 1, 5 stats_packets in = packets in stats_bytes in = bytes in stats_packets out = packets out stats_bytes out = bytes out stats_no takers tally = no takers tally stats_babl tally = babl tally stats_miss tally = miss tally stats_merr tally = merr tally stats_fram tally = fram tally stats_oflo tally = oflo tally stats_CRC tally = CRC tally stats_buff tally = buff tally stats_more tally = more tally stats_one tally = one tally stats_def tally = def tally stats_uflo tally = uflo tally stats_lcol tally = lcol tally stats_lcar tally = lcar tally stats_rtry tally = rtry tally r_bytes = 4 * 19 r_status = ether success %else %if r_code = ether show CSR0 ! Print out the contents of the Lance's CSR0 register printstring("LANCE: CSR0 = "); phex4(lance data) newline r_status = ether success %else r_status = ether undefined %finish send: send message(r, r_system part_reply, nil) %if r_system part_reply ## nil %end ! Main program. Start it going, then wait for user requests to interpret. %ownrecord(semaphore fm) request semaphore = 0 %ownrecord(mailbox fm) request mailbox = 0 %begin %record(ether request fm)%name r %record(process fm)%name p %integer i %label start RX, start TX ;!, start error open input(3, ":N"); select input(3) open output(3, ":T"); select output(3) %unless FS lookup("SLOW_ETHER_ADDRESS", i) %start printstring("Lance: Address unknown"); newline %stop %finish our ether address_x(0) = 'F'; our ether address_x(1) = 'R' our ether address_x(2) = 'E'; our ether address_x(3) = 'D' our ether address_x(4) = '-'; our ether address_x(5) = i & 255 setup semaphore(RX add semaphore) signal semaphore(RX add semaphore) setup semaphore(RX semaphore) setup semaphore(TX semaphore) setup semaphore(error semaphore) setup semaphore(inbound semaphore) signal semaphore(inbound semaphore) setup semaphore(request semaphore) setup mailbox(request mailbox, request semaphore) FS insert(ether mailbox name, addr(request mailbox)) FS insert("LANCE_CONTROL", addr(control)) p == create process(RX size, addr(start RX), RX priority, nil) p == create process(TX size, addr(start TX), TX priority, nil) !p == create process(error size, addr(start error), error priority, nil) initialise lance add RX to ring(record(global heap get(RX total size))) %c %for i = 1, 1, RX buffers {} printstring("LANCE: "); write(free store, 0) {} printstring(" free, address ") {} %for i = 0, 1, 5 %cycle {} print symbol('-') %if i # 0 {} phex2(our ether address_x(i)) {} %repeat {} newline %cycle r == receive message(request mailbox) interpret request(r) %repeat start RX: RX process start TX: TX process !start error: error process %end %of %program