#-h- srdat.mac 3042 asc 23-apr-81 06:56:04 [002,010] .title srdrv ; ; ; Send/receive driver for RSX-11M ; ; .ident /01/ ; ; ; Joe Sventek ; Computer Science and Applied Mathematics ; Lawrence Berkeley Laboratory ; Berkeley, CA 94720 ; ; 15-aug-80 ; ; ; This driver implements a variable length send/receive capability for ; RSX-11M. Packets are queued in internal buffers for a task which ; need not be installed. A timeout is associated with each message, ; and the buffers are recycled if the time expires before the message ; is read. ; ; macro library calls ; .mcall pktdf$,clkdf$,tcbdf$,ucbdf$,qiosy$ pktdf$ ; define I/O packet offsets clkdf$ ; define clock queue block offsets tcbdf$ ; define task control block offsets ucbdf$ ; define unit control block offsets qiosy$ ; define qio symbols locally .page .sbttl local symbols ; ; ; local symbols ; ; ; offsets for header blocks ; .asect .=0 h_flnk: .blkw 1 ; forward link h_blnk: .blkw 1 ; backward link h_stsk: .blkw 2 ; sender task name (rad50) h_rtsk: .blkw 2 ; receiver task name (rad50) h_size: .blkw 1 ; size of message in bytes h_time: .blkw 1 ; seconds until purge of message h_bufp: .blkw 1 ; pointer to first chained data buffer h_lgth=.-2 ; length of block minus flink word .psect ; ; ; local offset definition for clock queue block ; c.ucb=c.ar5+2 ; UCB address of SR: stored here ; this word unused in clock block c.cqid=c.tcb ; field for cqid ; ; ; local offset definitions for u.cw2 and u.cw3 in UCB ; u.hdrf=u.cw2 ; points to most recently inserted hdr u.hdrb=u.cw3 ; points to least recently inserted hdr ; ; ; local offset definitions for I/O request packet ; i.p1=i.prm ; p1 i.p2=i.prm+4 ; p2 i.p3=i.p2+2 ; p3 i.p4=i.p3+2 ; p4 i.p5=i.p4+2 ; p5 i.p6=i.p5+2 ; p6 ; ; ; definitions for timer processing (all in units of seconds) ; tdeflt=30. ; default timeout period for a packet tlow=2*tintpt ; minimum timeout period thigh=10.*60. ; maximum timeout period (10 minutes) .page .sbttl local macros ; ; ; local macros ; ; ; jcs - jump on c bit set ; .macro jcs dest,?lab bcc lab jmp dest lab: .endm jcs ; ; jeq - jump on equal (z bit set) ; .macro jeq dest,?lab bne lab jmp dest lab: .endm jeq ; ; push - push register onto stack ; .macro push reg mov reg,-(sp) .endm push ; ; ; pop - pop register from stack ; .macro pop reg mov (sp)+,reg .endm pop ; ; putnod - put a node back onto free list ; .macro putnod reg,listhd mov listhd,('reg') mov reg,listhd .endm putnod ; ; error - place return code in r0 and finish io ; .macro error reason mov #ie.'reason'&377,r0 callr zrfin .endm error .page .sbttl local data and dispatch table ; ; ; local data (except for headers and buffers, which follow driver code) ; ; hdrpt: .word hdr0 ; free list listhead for msg headers bufpt: .word buf0 ; free list listhead for buffers ; ; ; driver dispatch table ; ; $srtbl:: .word srini ; device initiator entry point .word srnop ; cancel io - NOOP .word srnop ; device timeout - NOOP .word srpwf ; powerfail #-h- srini.mac 1991 asc 15-apr-81 08:18:38 [002,010] .page .sbttl srini - initiate io for send/receive device ;+ ; srini - send/receive driver initiator ; ; this routine is entered from DRQIO when an io request is queued ; ; all operations are handled at queue level ; ; inputs ; r1 IRP of request ; r5 UCB of controller ; ; outputs ; if request is a write, message is queued, if buffer ; space is available ; ; if request is a read, the next message buffered for the ; requesting task is dequeued ; ; format of IRP for send/receive driver ; ; wd 00 io queue thread word ; wd 01 request priority, event flag number ; wd 02 TCB address of requestor ; wd 03 pointer to second LUN word in task header ; wd 04 contents of first LUN word in task header ; wd 05 io function code (io.rlb, io.wlb) ; wd 06 virtual address of io status block ; wd 07 relocation bias of io status block ; wd 10 io status block address (real or displacement + 140000) ; wd 11 virtual address of AST service routine ; wd 12 relocation bias of io buffer ; wd 13 buffer address (real or displacement + 140000) ; wd 14 number of bytes to transfer or size of buffer ; wd 15 high order half of receiver task name (WLB) ; wd 16 low order half (WLB) ; wd 17 time out period in seconds (WLB) ; wd 20 if non-zero, all packets queued for this sender/receiver ; pair are flushed before the current message is queued ; wd 21 unused ; ;- .enabl lsb srini: mov kisar6,-(sp) ; save current APR6 mapping mov r1,r3 ; place IRP in r3 for duration mov i.tcb(r3),r4 ; place TCB address for duration mov i.p2(r3),u.cnt(r5) ; place byte count in UCB movb i.fcn+1(r3),r0 ; fetch function code cmpb r0,#io.wlb/256. ; write logical block? jeq write ; yes, go do it cmpb r0,#io.rlb/256. ; read logical block? jeq read ; yes, go do it cmpb r0,#io.stp/256. ; stop timer? jeq stop ; yes, go do it mov #ie.ifc&377,r0 ; illegal function code zrfin: clr r1 ; iosb(2) = 0 on errors srfin: mov (sp)+,kisar6 ; restore mapping callr $iofin ; finish up .dsabl lsb #-h- write.mac 1751 asc 15-apr-81 08:00:13 [002,010] .page .sbttl process write requests ;+ ; this routine alocates a header and the necessary buffers. ; if all are successful, the user's data is then ; copied into the driver buffers, the header is linked into the UCB's ; queue. ; ; inputs ; r3 IRP address ; r4 TCB address ; r5 UCB address ;- .enabl lsb write: tst i.p3(r3) ; see if user supplied name bne 10$ ; if !=, yes tst i.p4(r3) ; check if second half is non-zero bne 10$ ; if !=, yes error bad ; bad parameters 10$: tst i.p6(r3) ; flush old messages? beq 20$ ; if == 0, NO call mflush ; flush them 20$: mov #hdrpt,r1 ; header free listhead in r1 call getpkt ; get a header into r0 jcs hdrerr ; c set => no more headers mov r0,r2 ; need header address in r2 mov i.p2(r3),h_size(r2) ; copy message size into header call getbuf ; allocate buffers for message jcs buferr ; c set => no room to buffer message mov i.p3(r3),h_rtsk(r2) ; copy receiver taskname into header mov i.p4(r3),h_rtsk+2(r2) mov t.nam(r4),h_stsk(r2) ; copy sender taskname into header mov t.nam+2(r4),h_stsk+2(r2); call usrsys ; copy user's data into buffers mov i.p5(r3),r0 ; get user supplied timeout bne 100$ ; if !=, got one mov #tdeflt,r0 ; default time limit in r0 100$: ; see if too large cmp r0,#thigh ble 110$ mov #thigh,r0 110$: ; see if too small cmp r0,#tlow bge 120$ mov #tlow,r0 120$: ; just right mov r0,h_time(r2) ; store time limit in header call inshdr ; insert header into UCB queue mov #is.suc&377,r0 ; success return mov i.p2(r3),r1 ; return byte count callr srfin ; finish up buferr: putnod r2,hdrpt ; return header to free list error alc ; buffers exhausted hdrerr: error nod ; headers exhausted .dsabl lsb #-h- read.mac 1174 asc 15-apr-81 08:00:15 [002,010] .page .sbttl process read requests ;+ ; scan through header list for oldest packet matching the ; taskname of the requestor. if found, copy the buffer to the user's ; buffer and recycle the header and driver buffers ; ; inputs ; r3 IRP address ; r4 TCB address ; r5 UCB address ;- .enabl lsb read: mov u.hdrb(r5),r2 ; oldest header block in r2 10$: beq 50$ ; last move == 0, no match cmp t.nam(r4),h_rtsk(r2) ; compare first half of task name bne 20$ ; if !=, try next header in list cmp t.nam+2(r4),h_rtsk+2(r2); compare second half of task name beq 30$ ; if ==, copy message 20$: mov h_blnk(r2),r2 ; get next header br 10$ ; try again 30$: call sysusr ; copy to user's buffer call putbuf ; return buffers to free list call remhdr ; remove header from UCB list putnod r2,hdrpt ; return header to free list mov #is.suc&377,r0 ; return success status mov h_size(r2),r1 ; bytes copied cmp r1,i.p2(r3) ; see if data overrun ble 40$ ; if <=, no mov #ie.dao&377,r0 ; return data overrun status mov i.p2(r3),r1 ; only copied usersize bytes 40$: callr srfin ; done with request 50$: error dna ; data not available .dsabl lsb #-h- stop.mac 1255 asc 15-jun-81 07:30:22 [307,034] .page .sbttl stop - stop timer ;+ ; this routine, called when a task issues a qio with a function ; code of io.stp, cancels the current timer request for the driver. ; It checks that the terminal for which the calling task ; is running is priveleged. If so, it proceeds to cancel the timer. ; ; inputs: ; r3 IRP address ; r4 TCB address ; r5 UCB address ; ; outputs: ; the clock queue entry for the driver is canceled and ; the clock queue control block is returned to the DSR ;- .enabl lsb stop: mov t.ucb(r4),r0 ; get TI: UCB address bit #u2.prv,u.cw2(r0) ; see if tty is priveleged bne 10$ ; if !=, YES - proceed error pri ; privelege violation 10$: mov u.clqb(r5),r0 ; place clock block addr in r0 beq 20$ ; if == 0, timer not active push r3 ; save r3 push r5 ; save r5 mov #c.syst,r4 ; clock queue request type mov r0,r5 ; clock queue id is block address call $clrsm ; remove entry from clock queue pop r5 ; restore UCB address mov u.clqb(r5),r0 ; address of block to return mov #c.lgth,r1 ; size of block call $deacb ; return block to DSR clr u.clqb(r5) ; timer no longer active pop r3 ; restore IRP address 20$: mov #is.suc&377,r0 ; return success status callr zrfin ; finish up .dsabl lsb #-h- srpwf.mac 1972 asc 15-apr-81 08:00:18 [002,010] .page .sbttl srpwf - powerfail entry point ;+ ; this routine is called when the system is booted, upon restoration ; of power, and when the driver is loaded, since the uc.pwf bit ; is set in the u.ctl field of the UCB ; ; this routine allocates a clock queue control block from pool ; it then fills it in and queues it ; the interrupt service routine propagates this interrupt. ; ; If the block cannot be allocated from pool, the driver indicates ; that the device is offline by setting the bit in the u.st2 field ; of the UCB and having TKTN issue a device not ready message on ; the console. ; ; inputs: ; r3 controller index ; r4 SCB address ; r5 UCB address ; ; outputs: ; 1. the delta time for each clock interrupt is calculated and ; stored in the UCB ; 2. the address of the allocated clock block is stored in the UCB ; 3. a clock interrupt is queued for the driver ; ;- .enabl lsb srpwf: tst u.clqb(r5) ; see if timer already active bne 20$ ; if !=, YES push r0 ; save all registers push r1 push r2 push r3 push r4 push r5 mov #tintpt,r0 ; seconds between clock interrupts mov $tkps,r1 ; clock ticks/second call $mul ; double word delta time in r0,r1 mov r0,u.tim1(r5) ; save in UCB mov r1,u.tim2(r5) mov #c.lgth,r1 ; size of clock queue core block call $alocb ; allocate from pool bcs clkerr ; c set => allocation failure mov r0,u.clqb(r5) ; save address of clock block mov r5,c.ucb(r0) ; save UCB addr in clock block mov #timer,c.sub(r0) ; address of interrupt service rtn mov u.tim1(r5),r1 ; delta time for $clins mov u.tim2(r5),r2 mov #c.syst,r4 ; clock queue request type mov r0,r5 ; clock queue id is block address call $clins ; insert into clock queue br 10$ clkerr: bisb #us.ofl,u.st2(r5) ; set device offline mov #t.ndnr,r0 ; issue a device not ready call $dvmsg ; to console via TKTN 10$: pop r5 ; restore registers pop r4 pop r3 pop r2 pop r1 pop r0 20$: return .dsabl lsb #-h- srnoop.mac 150 asc 15-apr-81 08:00:20 [002,010] .page .sbttl noop - cancel io and timeout entry points ;+ ; both of these entry points correspond to a return to the ; executive. ;- srnop: return #-h- timer.mac 1587 asc 15-apr-81 08:00:21 [002,010] .page .sbttl timer - process clock queue interrupts ;+ ; this routine is entered when the driver's clock queue entry comes ; due. it traverses the list of messages, decrementing the timeout ; field in the message headers. If the timeout field drops below 0, ; the header and message buffers are recycled. Another clock queue ; request is then made, thus propagating this self-cleansing process. ; ; inputs ; r4 address of clock queue core block ; ; outputs ; if matching header is found, that message is purged from list ; another clock queue request is made ;- .enabl lsb timer: push r0 ; this register manipulation is push r1 ; done to permit timer to call push r2 ; putbuf and remhdr, which conform push r3 ; to the internal register standard push r4 ; of the driver push r5 mov c.ucb(r4),r5 ; place UCB address in r5 mov u.hdrf(r5),r3 ; get first header address in r3 10$: mov r3,r2 ; get next header address beq 20$ ; if last move == 0, done scanning mov h_flnk(r2),r3 ; place next header addr in r3 sub #tintpt,h_time(r2) ; subtract no of seconds from limit bgt 10$ ; if > 0, try next packet call putbuf ; return buffers call remhdr ; remove header from queue putnod r2,hdrpt ; return header to free list br 10$ 20$: mov u.clqb(r5),r0 ; clock block in r0 mov u.tim1(r5),r1 ; delta time in r1-r2 mov u.tim2(r5),r2 mov #c.syst,r4 ; request type mov r0,r5 ; clock queue id is address of block call $clins ; insert into clock queue pop r5 ; restore registers pop r4 pop r3 pop r2 pop r1 pop r0 return .dsabl lsb #-h- mflush.mac 1180 asc 15-apr-81 08:01:31 [002,010] .page .sbttl mflush - flush old messages ;+ ; this routine traverses the list of messages, purging any matching ; the current sender-receiver task pair. The receiver name is in the ; I/O request packet, and the sender name is in the TCB ; ; inputs ; r3 IRP address ; r4 TCB address ; r5 UCB address ; ; outputs ; old messages matching the current task pair are purged ;- .enabl lsb mflush: mov u.hdrf(r5),r1 ; first header address in r1 10$: mov r1,r2 ; next header address in r2 beq 20$ ; if == 0, done scanning list mov h_flnk(r2),r1 ; next header address in r1 cmp i.p3(r3),h_rtsk(r2) ; first half of rcvr task? bne 10$ ; NO, try next message cmp i.p4(r3),h_rtsk+2(r2) ; second half of rcvr task? bne 10$ ; NO, try next message cmp t.nam(r4),h_stsk(r2) ; first half of sender task? bne 10$ ; NO, try next message cmp t.nam+2(r4),h_stsk+2(r2); second half of sender task? bne 10$ ; NO, try next message push r1 ; save volatile register call putbuf ; return message buffers call remhdr ; remove header from list putnod r2,hdrpt ; return header to free list pop r1 ; restore r1 br 10$ ; try next message 20$: return .dsabl lsb #-h- getbuf.mac 1317 asc 15-apr-81 08:01:33 [002,010] .page .sbttl getbuf - get buffers necessary to store message ;+ ; inputs: ; r2 header block ; r3 IRP address ; r4 TCB address ; r5 UCB address ; r0,r1 free for use ; ; outputs: ; c set insufficient buffer space ; c clear buffers were allocated. h_bufp has address of first ;- .enabl lsb getbuf: push r3 ; save registers used push r4 mov h_size(r2),r0 ; get number of bytes needed cmp r0,u.cw4(r5) ; see if within limits bgt errout ; too big, return dec r0 ; ((n-1)/B)+1 mov #bufsiz,r1 ; divisor in r1 call $div ; divide r0 by r1, quotient in r0 inc r0 ; r0 = number of bufs needed (>=1) mov r0,r3 ; save this number mov #bufpt,r1 ; buffer listhead 10$: mov (r1),r1 ; get next buffer address beq errout ; if == 0, not enough buffers sob r0,10$ ; decrement count and try again ; ; at this point, we are assured of enough buffers for message ; mov #bufpt,r1 ; listhead again clr r4 ; address of previous node 20$: call getpkt ; get node address in r0 mov r4,(r0) ; link in previous packets mov r0,r4 ; save address sob r3,20$ ; decrement count and try again mov r4,h_bufp(r2) ; place first buf addr in header clc ; c clear => success br 30$ errout: sec ; c set => allocation failure 30$: pop r4 ; restore registers - doesn't affect c bit pop r3 return .dsabl lsb #-h- putbuf.mac 619 asc 15-apr-81 08:01:34 [002,010] .page .sbttl putbuf - return buffers to free list ;+ ; putbuf restores the buffers pointer to by the header block to the ; free list of buffers ; ; inputs ; r2 header pointing to buffers ; r3 IRP address ; r4 TCB address ; r5 UCB address ; r0,r1 free for use ; ; outputs ; the buffers are returned to the free list of buffers ;- .enabl lsb putbuf: mov h_bufp(r2),r1 ; address of first buffer in r1 10$: mov r1,r0 ; address of next buffer in r0 beq 20$ ; if == 0, done mov (r0),r1 ; next buffer address in r1 putnod r0,bufpt ; return node to free list br 10$ ; do again 20$: return .dsabl lsb #-h- inshdr.mac 909 asc 15-apr-81 08:01:35 [002,010] .page .sbttl inshdr - inserts header into UCB queue ;+ ; this routine inserts the specified header into the queue pointed ; to by the UCB. The header is inserted on the forward link side ; of the UCB. Refer to the documentation on the driver for the ; exact structure of the headers and the linkages. ; ; inputs ; r2 header to insert ; r3 IRP address ; r4 TCB address ; r5 UCB address ; r0,r1 free for use ; ; outputs ; header is linked into UCB queue ;- .enabl lsb inshdr: mov u.hdrf(r5),r0 ; most recently inserted header beq 10$ ; queue was empty mov r2,h_blnk(r0) ; make that node point to this one clr h_blnk(r2) ; new node has no backward link mov r0,h_flnk(r2) ; new node points to old first node mov r2,u.hdrf(r5) ; UCB queue points to new node br 20$ 10$: mov r2,u.hdrf(r5) ; initialize new queue mov r2,u.hdrb(r5) clr h_flnk(r2) clr h_blnk(r2) 20$: return .dsabl lsb #-h- remhdr.mac 706 asc 15-apr-81 08:01:37 [002,010] .page .sbttl remhdr - remove header from UCB queue ;+ ; inputs ; r2 header to remove ; r3 IRP address ; r4 TCB address ; r5 UCB address ; r0,r1 free for use ; ; outputs ; header is removed from queue and queue is relinked ;- .enabl lsb remhdr: mov h_flnk(r2),r0 ; get forward link of ill-fated node beq 10$ ; if == 0, then end of list mov h_blnk(r2),h_blnk(r0) ; redo neighbor's blink br 20$ 10$: mov h_blnk(r2),u.hdrb(r5) ; redo listhead's blink 20$: mov h_blnk(r2),r0 ; get backward link of ill-fated node beq 30$ ; if == 0, then end of list mov h_flnk(r2),h_flnk(r0) ; redo neighbor's flink br 40$ 30$: mov h_flnk(r2),u.hdrf(r5) ; redo listhead's flink 40$: return .dsabl lsb #-h- sysusr.mac 1452 asc 15-apr-81 08:32:39 [002,010] .page .sbttl sysusr - copy contents of driver buffer to user ;+ ; this routine copies the contents of the driver buffer[s] into ; the user's buffer. If the user's buffer is smaller than the ; amount of data buffered, then only that many bytes are copied. ; When this occurs, the user receives the code ie.dao (data ; overrun) in iosb(1). ; ; inputs: ; r2 header address ; r3 IRP address ; r4 TCB address ; r5 UCB address ; r0,r1 free for use ; ; outputs: ; the data has been copied into the user's buffer ;- .enabl lsb sysusr: push r4 ; save registers push r2 ; ... cmp h_size(r2),u.cnt(r5) ; compare size of msg with user's buf bgt 10$ ; msg size too big, leave u.cnt as is mov h_size(r2),u.cnt(r5) ; only move bytes 10$: tst u.cnt(r5) ; see if zero length transfer beq 30$ ; if == 0, YES mov h_bufp(r2),r4 ; get first buffer address mov i.p1(r3),kisar6 ; map to user's buffer mov i.p1+2(r3),r2 ; kernel virtual address br 25$ ; start copy 20$: movb (r0)+,(r2)+ ; place byte in user buffer dec u.cnt(r5) ; decrement count beq 30$ ; if == 0, all done sob r1,20$ ; decrement bytes/buffer count mov (r4),r4 ; get next chained buffer beq 30$ ; if == 0, all done 25$: mov r4,r0 ; need buffer address in r0 tst (r0)+ ; bump pointer past link field mov #bufsiz,r1 ; bytes/buffer counter br 20$ ; move some more 30$: pop r2 ; restore registers pop r4 ; ... return .dsabl lsb #-h- usrsys.mac 1120 asc 23-apr-81 06:56:24 [002,010] .page .sbttl usrsys - copy contents of user buffer to driver ;+ ; this routine copies the contents of the user's buffer into ; the driver's internal buffer[s]. ; ; inputs: ; r2 header address ; r3 IRP address ; r4 TCB address ; r5 UCB address ; r0,r1 free for use ; ; outputs: ; the contents of the user's buffer are copied into the ; driver's buffer[s] ;- .enabl lsb usrsys: push r4 ; save registers push r2 ; ... tst u.cnt(r5) ; see if zero length transfer beq 20$ ; if == 0, YES mov h_bufp(r2),r4 ; address of first buffer mov i.p1(r3),kisar6 ; map to user's buffer mov i.p1+2(r3),r2 ; kernel virtual address br 15$ ; start copy 10$: movb (r2)+,(r0)+ ; copy byte into driver buffer dec u.cnt(r5) ; decrement count beq 20$ ; if == 0, all done sob r1,10$ ; decrement byte/buffer counter mov (r4),r4 ; address of next chained buffer beq 20$ ; if == 0, all done 15$: mov r4,r0 ; need address in r0 tst (r0)+ ; bump pointer past link field mov #bufsiz,r1 ; bytes/buffer br 10$ ; copy some more 20$: pop r2 ; restore registers pop r4 ; ... return .dsabl lsb #-h- getpkt.mac 633 asc 15-apr-81 08:01:41 [002,010] .page .sbttl getpkt - fetch a node from a free list ;+ ; this routine fetches a single node from a singly linked ; list. It is assumed that the forward link field in each node in the ; list is contained at an offset of 0 from the node address. ; ; inputs ; r1 listhead address ; ; outputs ; r0 node fetched from list ; c set if no more nodes left ; c clear if successfully fetched a node ;- .enabl lsb getpkt: mov (r1),r0 ; address of next node in r0 beq 10$ ; if == 0, no more nodes mov (r0),(r1) ; relink list clc ; c clear => success br 20$ 10$: sec ; c set => allocation failure 20$: return .dsabl lsb #-h- srbufs.mac 797 asc 15-apr-81 08:01:42 [002,010] .page .sbttl free lists of headers and buffers ;+ ; the free list of buffers and headers fill up the rest of the ; driver. ; The appropriate constants below may be changed if more or less ; of these data blocks are needed. ;- ; ; ; symbols ; ; bufsiz=32. ; number of bytes of data in each buffer avnbuf=3 ; average number of buffers per msg nbufs=avnbuf*nhdrs ; number of buffers ; ; ; macros for building linked lists ; ; .macro trmn8r str,num str'num=0 .endm trmn8r .macro bldnod str,size,num,nxt str'num: .word str'nxt .blkb size .endm bldnod ; ; ; build header list ; ; trmn8r hdr,\nhdrs ctr=0 .rept nhdrs bldnod hdr,h_lgth,\ctr,\ ctr=ctr+1 .endr ; ; ; build buffer list ; ; trmn8r buf,\nbufs ctr=0 .rept nbufs bldnod buf,bufsiz,\ctr,\ ctr=ctr+1 .endr .end