; ; lt_routines : LAT Application Port maniulation routines. ; ; Submitted by Robert Simon ; Network Equipment Technologies ; Santa Barbara, CA ; November 6, 1989 ; ; Abstract: When a program wants to utilize a serial port on ; a terminal server for general purpose communication, ; it uses LAT Application Ports. Before a program ; can use use the application port, it must successfully ; connect the application port to the appropriate ; terminal server port. The routines in this submission ; provide a simple interface for programs written in C, ; FORTRAN, or almost any VMS programming language ; to manipulate LAT application ports. ; These routines hide the QIO interface provided ; by VMS and provide user friendly procedure calls. ; Some C and FORTRAN examples are included. ; ; ; ; These routines are provided as part of the session notes for ; DECUS Fall 1989 Symposia, Anaheim, CA, session NE009, ; "VAX/VMS LAT Programming Made Simple". Included are some excerpts ; from that session which provide documentation for the routines. ; ; ; The routines: ; ; lt_init - allocates/opens the port and handle ; lt_map - sets up the destination for connection ; lt_connect - performs connection ; lt_hangup_ast - set up AST to be called on unexpected disconnect ; lt_conn_info - gets name of connected server port ; lt_disconnect - performs disconnection ; lt_close - closes/deallocates port and handle ; ; ; The lt_ routines: ; ; Handle busy conditions transparently ; Give detailed error messages ; Send error messages to SYS$OUTPUT (this can be changed by ; modifying the routine PUT_OUTPUT) ; Use special routines for descriptor oriented languages (for ; example FORTRAN) - lt_initd, lt_mapd, and lt_conn_infod ; Return 0 for failure, positive number for success ; ; ; ; lt_init: ; ; Opens a channel to the port ; Returns a handle for use with the other routines ; Must be called before any other lt_ routine ; Has alternate for lt_initd for FORTRAN ; example: ; handle = lt_init("LTA100:"); ; HANDLE = LT_INITD('LTA100:') ; ; parameters for lt_init: ; ; port_name - string passed by reference ; ; parameters for lt_initd: ; ; port_name - string passed by descriptor ; ; ; ; lt_map: ; ; Maps destination port information ; Not required, mapping can be done from LATCP ; Alternate for lt_mapd for FORTRAN ; Also sets port Queued or Not-Queued ; Returns success (Non-zero) or failure (Zero) ; Requires phy_io privilege ; For LAT, requires NODE NAME and either SERVICE NAME or PORT NAME ; format: ; status = lt_map(handle,node,service,port,link,queued) ; example: ; status = lt_map(handle,"MYSERVER",0,"PORT_2",0,0); ; ; parameters for lt_map: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; node - string passed by reference or NULL passed by value ; service - string passed by reference or NULL passed by value ; port - string passed by reference or NULL passed by value ; link - string passed by reference or NULL passed by value ; queued - integer (0 or 1) passed by value or reference. ; all parameters are read only ; ; parameters for lt_mapd: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; node - string passed by descriptor or NULL passed by value or reference ; service - string passed by descriptor or NULL passed by value or reference ; port - string passed by descriptor or NULL passed by value or reference ; link - string passed by descriptor or NULL passed by value or reference ; queued - integer (0 or 1) passed by value or reference. ; all parameters are read only ; ; ; lt_connect: ; ; Performs connection ; Returns success (non-zero) or failure (zero) ; example: ; status = lt_connect(handle); ; ; parameters for lt_connect: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; ; lt_disconnect: ; ; Performs disconnection ; Returns success (non-zero) even if port was not connected ; example: ; status = lt_disconnect(handle); ; ; parameters for lt_disconnect: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; ; ; ; ; ; lt_hangup_ast: ; ; Sets up a hangup AST to be called when unsolicited disconnect ; occurs ; Must be reset each time the AST is called ; Returns success (non-zero) or failure (zero) ; Actually sets up a control-y AST for the port, so the ; AST parameter should be checked for SS$_HANGUP ; A separate AST routine is required for each LAT port used, as there ; is no indication of which port disconnected. ; example: ; int ast_function(); ; sts = lt_hangup_ast(handle, ast_function) ; ; parameters for lt_hangup_ast: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; ast_function - routine entry point passed by reference ; ; ; ; ; lt_conn_info: ; ; Returns server port name to which LAT is connected ; Returns string NODE/PORT, or blank string if no connection ; Return value is length of string, 0 if no connection ; Alternate form lt_conn_infod for FORTRAN ; Buffer is assumed to be 34 characters long in lt_conn_info ; Server_name (up to 16 chars), '/' char, ; port_name (up to 16 chars), terminating NULL. ; Buffer will be NULL terminated ; example: ; len = lt_conn_info(handle,namebuf) ; LEN = LT_CONN_INFOD(HANDLE,NAMEBUF) ; ; parameters for lt_conn_info: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; namebuf - char array, passed by reference (will be modified) ; assumed to be at least 34 chars long ; ; parameters for lt_conn_infod: ; ; handle - longword passed by value or reference, returned ; by lt_init or lt_initd ; namebuf - char variable, passed by descriptor (any length, ; length is passed in as part of the descriptor) ; will be modified. ; ; ; ; ; lt_close: ; ; Closes and deallocates the port ; Frees up a slot to open another port (number of slots is ; assembly time parameter for lt_routines). ; Must be the last routine called for the port ; It is not necessary to call lt_close if the program is ; about to terminate anyway, image rundown will do it. ; example: ; status = lt_close(handle) ; parameter: handle is longword returned by lt_init or lt_initd ; ; ; ; ; ; ; ; Sample Program 1 ; ; ; main() ; { ; int handle1, sts, len; ; char buf [34]; /* buffer for lt_conn_info */ ; ; handle1 = lt_init("LTA100:"); /* Open the port */ ; sts = lt_map(handle1,"SERVER1","XPRINT",0,0,0); ; if (!sts) exit(); /* Just exit, error message was sent by lt_map */ ; sts = lt_connect(handle1); ; if (!sts) exit(); ; sts = lt_hangup_ast(handle1,disc_ast); ; if (!sts) exit(); ; len = lt_conn_info(handle1,buf); ; printf("Remote port =%s (len=%d)\n",buf,len); ; /* Here's where all the reads and writes get done */ ; /* IE the real work */ ; sts = lt_disconnect(handle1); ; sts = lt_close(handle1); ; } ; ; ; ; Sample Program 2, shows multiple LAT ports used ; ; int disc_ast1(int stat), disc_ast2(int stat); ; main() ; { ; int handle1, handle2; ; handle1 = lt_init("LTA100:"); ; handle2 = lt_init("LTA101:"); ; ; lt_connect(handle1); ; lt_connect(handle2); ; ; lt_hangup_ast(handle1,disc_ast1); ; lt_hangup_ast(handle2,disc_ast2); ; ; ... ; } ; ; Sample Program 3, FORTRAN ; ; PROGRAM LAST_TEST ; CHARACTER*34 BUF ; INTEGER HANDLE1,STS,LEN ; INTEGER LT_INITD,LT_MAPD,LT_CONNECT,LT_DISCONNECT,LT_CONN_INFOD,LT_CLOSE ; EXTERNAL LT_INITD,LT_MAPD,LT_CONNECT,LT_DISCONNECT,LT_CONN_INFOD,LT_CLOSE ; EXTERNAL HANGUP_AST_SUBR ; ; HANDLE1 = LT_INITD('LTA100:') ; STS = LT_MAPD(HANDLE1,'SERVER1','XPRINT',,,) ; STS = LT_CONNECT(HANDLE1) ; STS = LT_HANGUP_AST(HANDLE1,HANGUP_AST_SUBR) ; LEN = LT_CONN_INFOD(HANDLE1,BUF) ; TYPE *,' PORT=',BUF,' LEN=',LEN ; C DO READS AND WRITES AND USEFUL WORK HERE ; STS = LT_DISCONNECT(HANDLE1) ; STS = LT_CLOSE(HANDLE1) ; STOP ; END ; ; SUBROUTINE HANGUP_AST_SUBR(PAR) ; INTEGER PAR ; IF (PAR .EQ. SS$_HANGUP) TYPE *,' Hangup Seen' ; RETURN ; END ; ; ; ; ; lt_ routines for LAT application port manipulation ; ; Revision 1, November 3, 1989 ; Robert Simon ; Network Equipment Technologies (formerly Comdesign) ; Santa Barbara, CA 93117 ; ; ; lt_ routines. These routines are used to manipulate LAT and CCA ; applicaton ports. They are set up to be callable from common ; VAX/VMS languages and have been tested from FORTRAN and C. ; In the case where a string is passed in, the "d" form of the ; routine can be used from languages which pass in descriptors. ; ; A trick is used when the port "handle" is passed in and when ; the queuing flag is passed in to determine if the argument is ; being passed by value or by reference. Since these arguments ; should be less than 512 in all cases, if the argument is ; less than 512 it is presumed to have been passed by value, ; if it is larger, then it is assumed to have been passed by ; reference and it is dereferenced. ; ; These routines are provided for instructional purposes only. ; Network Equipment Technologies assumes no responsibility for ; the use of these routines or their fitness for use in any ; application. ; $IODEF GLOBAL .SBTTL Assembly Time Parameters ; ; Assembly Time Parameters ; ; MAX_PORTS - maximum number of LAT ports open at the same ; time. Should never exceed 511, otherwise the ; routines which detect if they're being called by ; reference or by value won't work right. ; MAX_PORTS=16 .SBTTL Static Data ; ; ; Static Data Variables ; ; CHAN_LIST - a channel for each open LAT port. Each channel ; is one word (2 bytes). The array of words is actually ; MAX_PORTS+1 long, since the first element (channel ; number 0) is never used, since returning a channel ; number of 0 would look like an error return. ; .psect ltr_data long,noexe,wrt CHAN_LIST: .BLKW ; never give out slot 0 ; ; Static Data Constants ; ; Lists of messages used to print out a message when the ; connect QIO request returns status of SS$_ABORT ; ABT_LIST: .long rsn0 ; array of pointers to descriptors .long rsn1 .long rsn2 .long rsn3 .long rsn4 .long rsn5 .long rsn6 .long rsn7 .long rsn8 .long rsn9 .long rsn10 .long rsn11 .long rsn12 .long rsn13 .long rsn14 .long rsn15 .long rsn16 .long rsn17 .long rsn18 .long rsn19 ; Descriptors for reason messages ; ; rsn0: .ascid /Unknown failure reason=0/ rsn1: .ascid /Unknown failure reason=1/ rsn2: .ascid /System shutdown in progress/ rsn3: .ascid /Unknown failure reason=3/ rsn4: .ascid /Unknown failure reason=4/ rsn5: .ascid /Insufficient resources at server/ rsn6: .ascid /Port or service in use/ rsn7: .ascid /No such service/ rsn8: .ascid /Service is disabled/ rsn9: .ascid /Service is not offered on the requested port/ rsn10: .ascid /Port name is unknown/ rsn11: .ascid /Unknown Failure Reason=11/ rsn12: .ascid /Unknown Failure Reason=12/ rsn13: .ascid /Immediate access rejected, invalid password/ rsn14: .ascid /Access denied, group code conflict/ rsn15: .ascid /Corrupted request/ rsn16: .ascid /Requested function is not supported/ rsn17: .ascid /Session cannot be started/ rsn18: .ascid /Queue entry deleted by server/ rsn19: .ascid /Illegal request parameters/ ; Message to print out when Connect QIO returns status SS$_TIMEOUT ; to_err: .ascid /Connect timeout, requested resource not found/ .psect ltr_code long,exe,nowrt ; ; NAME_SIZE = how long a name may be ; NAME_SIZE=16 .SBTTL lt_init, open and allocate port ; ; lt_init - This is the first routine called. It allocates a ; channel for the port and opens the channel. ; Inputs: ; lt_init: Null terminated string with port name DDCU: ; lt_initd: Descriptor of string with port name DDCU: ; ; init_id: .ascid /LT_INIT/ ; Name string for error messages chan_full: .ascid /LT_INIT: No free channel slots/ ; error message LI_PNAME=4 ; offset of port name argument .global LT_INIT ; Entry for lt_init .entry LT_INIT,^M MOVAB GET_STRING,R6 ; use GET_STRING() to get args BRB lt_init_xxx .global LT_INITD ; Entry for lt_initd .entry LT_INITD,^M MOVAB GET_DESC,R6 ; use GET_DESC() to get arguments BRB lt_init_xxx lt_init_xxx: CLRQ -(SP) ; quadword to build descriptor on stack MOVAB (SP),R4 ; descriptor address MOVZBL #1,R5 ; return value - assume success ;; try to allocate a free channel MOVZBL #1,R3 ; search free channel list 1$: TSTW CHAN_LIST[R3] ; free channel? BEQL 10$ ; yes AOBLEQ #MAX_PORTS,R3,1$ ; no, loop and try again PUSHAB chan_full ; no free chans, show error msg CALLS #1,PUT_OUTPUT ; write it out CLRL R5 ; set return code to error status BRW 99$ ; and be done 10$: MOVL R3,R5 ; set up to return chan number ;; build a descriptor for the name and assign the channel PUSHL LI_PNAME(AP) ; get port name argument CALLS #1,(R6) ; call string arg processing routine MOVQ R0,(R4) ; move descriptor onto local stack $ASSIGN_S DEVNAM=(R4),chan=CHAN_LIST[R3] ; assign chan ; check assign status BLBS R0,99$ ; we did it, we're done CLRL R5 ; failed, set failure return status PUSHL R0 ; push bad status PUSHAB init_id ; push our routine name CALLS #1,PUT_OUTPUT ; print our routine name CALLS #1,PUT_MESSAGE ; format our bad status 99$: MOVL R5,R0 ; return status into R0 RET ; and be done .SBTTL lt_connect, perform connection ; ; lt_connect - called to initiate a connection on a LAT ; application port. Called with a handle which ; has been allocated by lt_init ; Input: ; Handle which was returned by lt_init or lt_initd ; ; Note that if the connection fails with SS$_DEVOFFLINE, the ; connection will be tried once per second for nine additional ; seconds, since sometimes the port is still disconnecting ; from last time when a new connection is attempted. LTC_HANDLE=4 ; handleis first arguement connect_id: .ascid /LT_CONNECT/ ; name for error messages .global LT_CONNECT .entry LT_CONNECT,^M MOVZBL #1,R5 ; return value - assume success CLRQ -(SP) ; Allocate an IOSB on the stack MOVAB (SP),R2 ; save address of IOSB in R2 MOVL #10,R3 ; retry counter, 10 times 1$: ;; loop: try 10 times to connect, wait 1 second between tries MOVL LTC_HANDLE(AP),R1 ; get the handle CMPL R1,#512 ; is it a handle or an addres? BLSSU 4$ ; it's less than 512, it's a handle MOVL (R1),R1 ; it's an address, get the handle 4$: MOVZBL R1,R1 ; Mask unwanted bytes $QIOW_S - ; initiate connection IOSB=(R2),- FUNC=#,- CHAN=CHAN_LIST[R1] BLBC R0,20$ ; check return status - bad status MOVZWL (R2),R0 ; return status ok, get IOSB status BLBS R0,99$ ; ok IOSB status, all done 20$: ; failure, some kind of error CMPW R0,#SS$_DEVOFFLINE ; special case?, wait and retry BNEQ 21$ ; no, not spec case, some other error DECL R3 ; decrement retry counter BLSS 21$ ; branch if ran out of retries CALLS #0,WAIT_SEC ; still retrying,wait one second BRW 1$ ; and go try again 21$: ; didn't connect, return failure status, show error msg MOVZWL R0,R4 ; save error status into R4 CLRL R5 ; set return code to failure status PUSHAB connect_id ; routine name string for error msg CALLS #1,PUT_OUTPUT ; print out routine name CMPW R4,#SS$_TIMEOUT ; timeout? (If so then special msg) BNEQ 25$ ; no, go check for other errors PUSHAB to_err ; yes, use special timeout msg CALLS #1,PUT_OUTPUT ; print it out BRW 99$ ; and be done 25$: CMPW R4,#SS$_ABORT ; abort status? (If so, show reason) BNEQ 30$ ; no, skip reason message MOVZBL 2(R2),R0 ; get abort reason from IOSB+2 PUSHL ABT_LIST[R0] ; get descriptor for message CALLS #1,PUT_OUTPUT ; show it 30$: PUSHL R4 ; get error status CALLS #1,PUT_MESSAGE ; format error message 99$: MOVL R5,R0 ; put return status into R0 RET ; done .SBTTL lt_close, close and deallocate port ; ; lt_close - deallocate the handle, close the channel ; ; Input: Handle returned by lt_init ; close_id: .ascid /LT_CLOSE/ .global lt_close .entry lt_close,^M MOVL LC_HANDLE(AP),R2; get the handle CMPL R2,#512 ; is it a handle or an addres? BLSSU 4$ ; less than 512, it's a handle MOVL (R2),R2 ; it's an address, get handle 4$: MOVZBL R2,R2 ; Mask unwanted bytes MOVZBL #1,R5 ; assume success $DASSGN_S CHAN=CHAN_LIST[R2] ; deassign the channel BLBC R0,20$ ; failure, go print message CLRW CHAN_LIST[R2] ; success, free channel ind BRW 99$ ; done 20$: CLRL R5 ; failure, set return code PUSHL R0 ; push bad status PUSHAB close_id ; push routine name CALLS #1,PUT_OUTPUT ; show routine name CALLS #1,PUT_MESSAGE ; format bad status 99$: MOVL R5,R0 ; return status into R0 RET ; done .SBTTL lt_disconnect, perform disconnection ; ; lt_disconnect - initiate disconnect ; ; Inputs: Handle which was returned by lt_init ; LTD_HANDLE=4 ; handle is first argument disc_id: .ascid /LT_DISCONNECT/ .global LT_DISCONNECT .entry LT_DISCONNECT,^M MOVZBL #1,R5 ; return status - assume success CLRQ -(SP) ; allocate IOSB on local stack MOVAB (SP),R2 ; IOSB address into R2 MOVL LTD_HANDLE(AP),R1 ; get the handle CMPL R1,#512 ; is it a handle or an addres? BLSSU 4$ ; less than 512, it's a handle MOVL (R1),R1 ; it's an address, get the handle 4$: MOVZBL R1,R1 ; Mask unwanted bytes $QIOW_S - ; initiate disconnect IOSB=(R2),- FUNC=#,- CHAN=CHAN_LIST[R1] BLBC R0,20$ ; check return status - bad status MOVZWL (R2),R0 ; return ok, get IOSB status BLBS R0,99$ ; ok IOSB status, all done 20$: ;; some kind of error CLRL R5 ; failure return status PUSHL R0 ; bad status PUSHAB disc_id ; routine name string CALLS #1,PUT_OUTPUT ; show routine name string CALLS #1,PUT_MESSAGE ; format bad status 99$: MOVL R5,R0 ; return status into R0 RET ; done .SBTTL lt_map, set destination and queuing option ; lt_map - set up connection destination and queuing option. ; Calling the QIO requires phy_io privilege. ; ; format: lt_map(handle, node, serv, port, link, q_option) ; or lt_mapd(handle, node, serv, port, link, q_option) ; ; String inputs may be passed as strings, zeroes or omitted ; args. C language zeroes and FORTRAN omitted args are 0 ; passed by value which is checked. FORTRAN 0 is 0 passed ; by reference which looks like a 0 length descriptor. ; map_id: .ascid /LT_MAP/ ; routine name for messages def_link: .ascid /LAT$LINK/ ; default link in case caller ; omits it (QIO requires it) I3_BUFLEN=0 ; format of itemlist type 3 I3_ITM=2 ; item code I3_PTR=4 ; string pointer I3_RETLEN=8 ; return length (not used for map_port QIO) I3_ENTRY=12 ; size of one entry LM_HANDLE=4 ; argument list LM_NOD=8 ; node name LM_SRV=12 ; service name LM_POR=16 ; port name LM_LNK=20 ; link name LM_QUE=24 ; queuing option .global lt_map,lt_mapd .entry lt_map,^M MOVAB GET_STRING,R6 ; null term string routine BRW lt_map_xxx .entry lt_mapd,^M MOVAB GET_DESC,R6 ; desc string routine BRW lt_map_xxx lt_map_xxx: ; make room for itemlist SUBL #,SP ; with 5 entries on stack CLRQ -(SP) ; room for IOSB MOVAB (SP),R4 ; address of IOSB & item list MOVAB 8(R4),R5 ; pointer to 1st itmlst entry ; process link entry - use default if none given PUSHL LM_LNK(AP) ; get link addr CALLS #1,(R6) ; process the arg TSTL R0 ; any link specified? BNEQ 10$ ; yes MOVQ def_link,R0 ; No, use default link desc 10$: MOVQ R0,(R5) ; fill in buflen and pointer MOVW #IO$V_LT_MAP_LNKNAM,I3_ITM(R5) ; fill in itemcode CLRL I3_RETLEN(R5) ; retlen address is not used MOVAB I3_ENTRY(R5),R5 ; advance to next entry ; process node if present PUSHL LM_NOD(AP) ; get node argument CALLS #1,(R6) ; process it TSTL R0 ; any node name? BEQL 20$ ; no, skip it MOVQ R0,(R5) ; fill in buflen and pointer MOVW #IO$V_LT_MAP_NODNAM,I3_ITM(R5) ; fill in item code CLRL I3_RETLEN(R5) ; retlen isn't used MOVAB I3_ENTRY(R5),R5 ; advance to next entry 20$: ; process port if present PUSHL LM_POR(AP) ; get port argument CALLS #1,(R6) ; process it TSTL R0 ; any port name? BEQL 30$ ; no, skip it MOVQ R0,(R5) ; fill in buflen and pointer MOVW #IO$V_LT_MAP_PORNAM,I3_ITM(R5) ; fill in item code CLRL I3_RETLEN(R5) ; retlen isn't used MOVAB I3_ENTRY(R5),R5 ; advance to next entry 30$: ; process service if present PUSHL LM_SRV(AP) ; get service name argument CALLS #1,(R6) ; process it TSTL R0 ; any serv name? BEQL 40$ ; no MOVQ R0,(R5) ; fill in buflen and pointer MOVW #IO$V_LT_MAP_SRVNAM,I3_ITM(R5) ; fill in item code CLRL I3_RETLEN(R5) ; retlen isn't used MOVAB I3_ENTRY(R5),R5 ; advance to next entry 40$: ; terminate list CLRL (R5) ; longword of 0 ; get the handle argument MOVL LM_HANDLE(AP),R1 ; get the handle CMPL R1,#512 ; is it a handle or an addres? BLSSU 44$ ; less than 512, it's a handle MOVL (R1),R1 ; it's an address, get handle 44$: MOVZBL R1,R1 ; Mask unwanted bytes ; get the queuing option MOVL LM_QUE(AP),R2 ; get queueing arg CMPL R1,#512 ; Is it q bit or an address? BLSSU 45$ ; less than 512, it's q bit MOVL (R2),R2 ; it's an address, get q bit 45$: MOVZBL R2,R2 ; Mask unwanted bits $QIOW_S P2=R2,- ; queuing option P1=8(R4),- ; itemlist IOSB=(R4),- FUNC=#,- CHAN=CHAN_LIST[R1] MOVZBL #1,R5 ; ret status,assume success BLBC R0,80$ ; return value, failure MOVZWL (R4),R0 ; ok return, get IOSB status BLBS R0,99$ ; IOSB ok, done 80$: PUSHL R0 ; push error code PUSHAB map_id ; push routine name CALLS #1,PUT_OUTPUT ; print routine name CALLS #1,PUT_MESSAGE ; format error code CLRL R5 ; show failure ret code 99$: MOVL R5,R0 ; ret code into R0 RET ; done .SBTTL lt_conn_info, get remote port name ; ; lt_conn_info - returns name of terminal server port to which ; this port is connected. ; ; Inputs: Handle - returned by lt_init ; Dest - buffer (lt_conn_info) or descriptor ; pointing to buffer (lt_conn_infod) ; CI_DEF_BUF_SIZE=<+2> ; room for server name, /, port name, and NULL LC_HANDLE=4 ; argument list - handle LC_DEST=8 ; dest string or buffer conn_info_id: .ascid /LT_CONN_INFO/ ; routine name for error msg .global lt_conn_info,lt_conn_infod .entry lt_conn_info,^M ; entry for string CLRL R8 ; R8: 0=str, 1=desc BRW lt_conn_info_xxx .entry lt_conn_infod,^M ; entry for desc MOVZBL #1,R8 ; R8: 0=str, 1=desc BRW lt_conn_info_xxx lt_conn_info_xxx: SUBL #,SP ; room for 2 entry itemlist MOVAB (SP),R4 ; address of itemlist in R4 SUBL #64,SP ; room for getdvi return MOVAB (SP),R5 ; string, put address in R5 CLRQ -(SP) ; alloc IOSB on local stack MOVAB (SP),R6 ; address of IOSB into R6 ; build itemlist MOVL #64,I3_BUFLEN(R4) ; buffer len (max return is 64 chars) MOVW #DVI$_TT_ACCPORNAM,I3_ITM(R4) ; item code MOVAB (R5),I3_PTR(R4) ; point to buffer MOVAB I3_BUFLEN(R4),I3_RETLEN(R4) ; use buflen for ret len CLRL I3_ENTRY(R4) ; longword of 0 at end MOVL LC_HANDLE(AP),R1 ; get the handle CMPL R1,#512 ; is it a handle or an addres? BLSSU 4$ ; less than 512, it's a handle MOVL (R1),R1 ; it's an address, get handle 4$: MOVZBL R1,R1 ; Mask unwanted bytes $GETDVIW_S CHAN=CHAN_LIST[R1],ITMLST=(R4),IOSB=(R6) BLBC R0,20$ ; return status failed MOVZWL (R6),R0 ; return ok, check IOSB BLBC R0,20$ ; IOSB failed MOVZWL (R4),R0 ; string 0 len? BLEQ 30$ ; yes, no error but ; return blank string BRW 80$ ; go copy the string 20$: ; an error, show error message PUSHL R0 ; push error code PUSHAB conn_info_id ; push routine name CALLS #1,PUT_OUTPUT ; print routine name CALLS #1,PUT_MESSAGE ; format error message ; error or 0 len string, set up for 0 len copy 30$: CLRL R0 ; No string, copy 0 chars 80$: MOVL #CI_DEF_BUF_SIZE,R1 ; assume user buffer is default size(34) MOVL LC_DEST(AP),R2 ; assume pointer to user buf BLBC R8,81$ ; using strings, no user len MOVZWL (R2),R1 ; use len from desc ; fill in user's buffer, null padded MOVL 4(R2),R2 ; use desc pointer 81$: PUSHR #^M MOVC5 R0,(R5),#0,R1,(R2) ; copy to user's buffer POPR #^M MOVL R0,R0 ; return length that's in R0 RET .SBTTL lt_hangup_ast, set up a hangup AST ; ; ; lt_hangup_ast - set up a hangup AST to be called if there's an ; unexpected hangup ; Inputs: ; handle - returned from lt_init ; ast address - address of function to call ; LH_AST=8 ; argument list, hangup AST LH_HANDLE=4 ; handle hangup_id: .ascid /LT_HANGUP/ ; routine name for error msgs .global lt_hangup_ast .entry lt_hangup_ast,^M MOVZBL #1,R5 ; return status, assume success CLRQ -(SP) ; allocate IOSB on local stack MOVAB (SP),R2 ; address of IOSB into R2 MOVL LH_HANDLE(AP),R1 ; get the handle CMPL R1,#512 ; is it a handle or an addres? BLSSU 4$ ; less than 512, it's a handle MOVL (R1),R1 ; it's an address, get the handle 4$: MOVZBL R1,R1 ; Mask unwanted bytes $QIOW_S - P1=@LH_AST(AP),- ;P1=AST IOSB=(R2),- FUNC=#,- CHAN=CHAN_LIST[R1] BLBC R0,20$ ; return stat, bad status MOVZWL (R2),R0 ; return ok, check IOSB BLBS R0,99$ ; ok IOSB status 20$: CLRL R5 ; failure status PUSHL R0 ; push bad status PUSHAB hangup_id ; push routine name CALLS #1,PUT_OUTPUT ; show routine name CALLS #1,PUT_MESSAGE ; format bad status 99$: MOVL R5,R0 ; return status into R0 RET ; done .SBTTL UTILITY ROUTINES ; ; ; *** Utility Routines **** ; ; ; ; get string len - returns length of null terminated string .entry GET_STR_LEN,^M LOCC #0,#24,@4(AP) ; look for null char SUBL3 R0,#24,R0 ; len = 24-chars left RET ; ; PUT_MESSAGE - formats an error message and writes it out ; Inputs: ; VMS error code ; ; local stack PM_SDESC=0 ; descriptor for output string PM_STRING=8 ; buffer for string conv_err: .ascid /Put Message conversion error/ .entry PUT_MESSAGE,^M SUBL #<128+8>,SP ; make room for local variables MOVAB (SP),R4 ; local stack addressability MOVZBL #128,PM_SDESC(R4) ; desc.buflen MOVAB PM_STRING(R4),PM_SDESC+4(R4) ; desc.ptr $GETMSG_S MSGID=4(AP),- ; error code MSGLEN=PM_SDESC(R4),- ; out put len BUFADR=PM_SDESC(R4),- ; input descriptor FLAGS=#^X0F ; flags (show all) MOVAB PM_SDESC(R4),R2 ; assume use of formatted string BLBS R0,10$ ; formatted OK MOVAB conv_err,R2 ; error, use generic error string 10$: PUSHL R2 ; push string CALLS #1,PUT_OUTPUT ; show string RET ; done ; ; PUT_OUTPUT - writes output of error messages to the user. ; ; This is a jacket routine, all output goes through this ; routine. It can be modified to send output other places. ; ; Inputs: descriptor of string to be printed ; .entry PUT_OUTPUT,^M<> ; routine to write to output device PUSHL 4(AP) ; push our one arg CALLS #1,G^LIB$PUT_OUTPUT ; call output routine RET ; done ; ; WAIT_SEC - waits one second ; wait_err: .ascid /WAIT_SEC_ERR/ .global WAIT_SEC .entry WAIT_SEC,^M CLRQ -(SP) ; place for local vars (time and ef) MOVAB (SP),R4 ; address of local vars PUSHAL (R4) ; use local for EF return value CALLS #1,G^LIB$GET_EF ; get an event flag BLBC R0,101$ ; failed, skip it MOVZBL (R4),R2 ; put EF into R2 MOVL #-1000*1000*10,(R4) ; use local var for time, 1 sec MOVL #-1,4(R4) ; high part $SETIMR_S EFN=R2,daytim=(R4) ; set the timer BLBC R0,101$ ; failed, skip it $WAITFR_S EFN=R2 ; wait for the flag BLBC R0,101$ ; failed, skip it MOVL R2,(R4) ; set up addr of ef PUSHAB (R4) ; push it CALLS #1,G^LIB$FREE_EF ; free the ef 99$: RET ; done 101$: ; error, show it PUSHL R0 ; error code PUSHAB wait_err ; routine name CALLS #1,PUT_OUTPUT ; show routine name CALLS #1,PUT_MESSAGE ; show error code BRW 99$ ; done ; GET_STRING and GET_DESC are used to get string arguments ; to functions. Both return a descriptor in R0-R1 ; .global GET_STRING ; get a null term string .entry GET_STRING,^M CLRQ R0 ; assume no string MOVL 4(AP),R2 ; get the arg BEQL 99$ ; no arg, done PUSHL R2 ; got an arg CALLS #1,GET_STR_LEN ; get the length in R0 MOVL R2,R1 ; put adress into R1 99$: RET ; done .global GET_DESC ; get descriptor string .entry GET_DESC,^M CLRQ R0 ; assume no string TSTL 4(AP) ; test the arg BEQL 99$ ; no arg, done MOVQ @4(AP),R0 ; get the desc into R0,R1 99$: RET ; done .end