%TITLE 'SMIIOGEN, SMI object to serve IOGEN functions' MODULE SMIIOGEN (IDENT = 'X-8', ENVIRONMENT(NOFP)) = BEGIN ! ! Copyright © Digital Equipment Corporation, 1992, 1994 All Rights Reserved. ! Unpublished rights reserved under the copyright laws of the United States. ! ! The software contained on this media is proprietary to and embodies the ! confidential technology of Digital Equipment Corporation. Possession, use, ! duplication or dissemination of the software and media is authorized only ! pursuant to a valid written license from Digital Equipment Corporation. ! ! RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure by the U.S. ! Government is subject to restrictions as set forth in Subparagraph ! (c)(1)(ii) of DFARS 252.227-7013, or in FAR 52.227-19, as applicable. ! !++ ! COMPONENT: ! ! SMI ! ! MODULE DESCRIPTION: ! ! This module contains the IOGEN SMI object dispatcher as well as all of the ! "SHOW" code that sits behind the SYSMAN IO SHOW command. ! ! AUTHOR: ! ! Richard W. Critz, Jr. ! ! CREATION DATE: 26-May-1992 ! ! ! MODIFICATION HISTORY: ! ! ! X-8 WDA W.D. Arbo 07-Oct-1996 ! Add support for /VERIFY to the REBUILD command. ! ! X-7 WDA W.D. Arbo 25-Sep-1996 ! Remove the IFOPEN, IFCLOSE, IFGETS dummy routines. ! ! X-6 WDA W.D. Arbo 13-Aug-1996 ! Add code to implement IO REBUILD. ! ! X-5 TGA Thomas G. Arnold 17-Jul-1996 ! Fix for EVMS-GRYPHON QAR 194. ! Processing in PROCESS_ADP() was improperly recursing when ! the NOSTACK flag was set. This caused incorrect printouts ! of bus configuration for SYSMAN IO SHOW BUS on Turbo-Lasers. ! ! X-4 RWC141 Richard W. Critz, Jr. 24-Jan-1994 ! Add support for SET/SHOW EXCLUSION. ! ! X-3 RWC113 Richard W. Critz, Jr. 29-Jan-1993 ! Deal with optional arugments on remote call. Update call to ! translate_hw_id to new interface using all 64-bits of the ! hardware id. Do bus specific masking of hardware identifiers ! during data collection in PROCESS_ADP. ! ! X-2 RWC092 Richard W. Critz, Jr. 9-Jul-1992 ! Expand the field width for the unit number in the SHOW DEVICE ! display. ! ! X-1 RWC082 Richard W. Critz, Jr. 26-May-1992 ! Initial version. ! !-- ! ! TABLE OF CONTENTS: ! FORWARD ROUTINE smi$iogen_init, smi$iogen_term, smi$iogen, autolog, show_dev, show_dev_next, show_bus, process_adp : NOVALUE, show_bus_next, show_log; ! ! INCLUDE FILES: ! LIBRARY 'sys$library:lib'; REQUIRE 'lib$:smidef'; REQUIRE 'src$:smiintdef'; REQUIRE 'lib$:smiiogendef'; ! ! UNDECLARE this macro. We need the routine of the ! same name, but not the macro. ! UNDECLARE %QUOTE iogen$build_config_table; ! ! MACROS: ! MACRO iosb_w_status = 0,0,16,0 %, iosb_w_flags = 2,0,16,0 %, iosb_ps_dpt = 4,0,32,1 %, busarray$l_hw_id = $byteoffset(busarray$q_hw_id),0,32,0 %, busarray$l_hw_id_hi = $byteoffset(busarray$q_hw_id),32,32,0 %, kernel_call (kroutine) [] = BEGIN EXTERNAL ROUTINE sys$cmkrnl; LOCAL args : VECTOR[%LENGTH,LONG,SIGNED] INITIAL(LONG(%LENGTH-1 %IF %LENGTH GTR 1 %THEN , %REMAINING %FI)); sys$cmkrnl(kroutine, args) END %, next_longword(ptr) = (((ptr)+3) AND NOT 3) %, read_long(arg) = arg = .data[0]; data = .data + 4 %, store_long(arg) = .data = (arg); data = .data + 4 %; ! ! EQUATED SYMBOLS: ! ! ! OWN STORAGE: ! OWN log_where : REF BLOCK[,BYTE]; ! ! EXTERNAL REFERENCES: ! LINKAGE sch$iolockr_linkage = JSB (REGISTER = 4), ! address of the PCB sch$iounlock_linkage = JSB (REGISTER = 4) : ! address of the PCB NOPRESERVE (0,1,2,3); EXTERNAL smi$ga_context : REF BLOCK[,BYTE], scs$ar_localsb : SIGNED LONG, ctl$gl_pcb : SIGNED LONG, ioc$gl_dptlist : SIGNED LONG, ioc$gl_adplist : SIGNED LONG; EXTERNAL ROUTINE smi$common_setup, smi$remote_call, sys$load_driver, ioc$init_adp_config, iogen$autoconfigure, iogen$build_config_table, iogen$delete_config_table, iogen$set_prefix, iogen$get_prefix, iogen$set_exclusion, iogen$get_exclusion, lib$get_vm, lib$free_vm, sch$iolockr : sch$iolockr_linkage, sch$iounlock : sch$iounlock_linkage, translate_adptype, translate_hw_id, ini$brk : NOVALUE; %SBTTL 'SMI$IOGEN_INIT, allocate and initialize object work area' GLOBAL ROUTINE smi$iogen_init(owa : REF VECTOR) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This is the standard SMI object initialization routine. It allocates and ! initializes an object work area. ! ! FORMAL PARAMETERS: ! ! owa - address of cell in which to store the object work area address ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SS$_NORMAL or status from LIB$GET_VM ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL status, iogenwa : REF BLOCK[,BYTE]; IF NOT (status = lib$get_vm(%REF(iogenwa$k_length), iogenwa)) THEN RETURN .status; CH$FILL(0, iogenwa$k_length, .iogenwa); iogenwa[iogenwa$ps_next] = iogenwa[iogenwa$b_data]; .owa = .iogenwa; RETURN ss$_normal END; %SBTTL 'SMI$IOGEN_TERM, deallocate the object work area' GLOBAL ROUTINE smi$iogen_term(owa : REF VECTOR) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This is a standard SMI object termination routine. It deallocates the ! object work area. ! ! FORMAL PARAMETERS: ! ! owa - address of the object work area ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SS$_NORMAL or status from LIB$FREE_VM ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL status; IF NOT (status = lib$free_vm(%REF(iogenwa$k_length), .owa)) THEN RETURN .status; .owa = 0; RETURN ss$_normal END; %SBTTL 'SMI$IOGEN, IOGEN object dispatcher' GLOBAL ROUTINE smi$iogen( smictx, ! SMI context block function, ! SMI function code flags, ! autoconfigure/show flags string1 : REF BLOCK[,BYTE], ! first string argument string2 : REF BLOCK[,BYTE], ! second string argument show_buf : REF BLOCK[,BYTE], ! output display buffer items : REF BLOCK[,BYTE], ! $LOAD_DRIVER item list bus_list : REF BLOCK[,BYTE], ! AUTOCONFIGURE bus list dpt_addr, ! returned DPT address show_len) = ! remote object kludge !++ ! FUNCTIONAL DESCRIPTION: ! ! This is the main dispatcher for the IOGEN SMI object. The interpretation of ! the flags, string1 and string2 formal parameters are dependent upon the ! function code. In the description below, they are described in the form: ! ! function: meaning, function: meaning ! ! FORMAL PARAMETERS: ! ! smictx - the SMI context block's address ! function - SMI function code ! flags - AUTO: AUTOCONFIGURE's flags ! SHOW/SHOW_NEXT: show what? ! string1 - CONNECT: device name ! AUTO: select list ! MODIFY: new prefix list ! string2 - CONNECT/LOAD/RELOAD: driver name ! AUTO: exclude list ! show_buf - buffer for output display ! items - item list for SYS$LOAD_DRIVER ! bus_list - bus list for IOGEN$AUTOCONFIGURE, encapsulated by a standard ! string descriptor ! dpt_addr - cell in which to return the DPT address after $LOAD_DRIVER ! calls ! show_len - actual length of show buffer on a remote call ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SS$_NORMAL ! SS$_NOPRIV ! SS$_ABORT ! any status value from: ! SMI$COMMON_SETUP ! SMI$REMOTE_CALL ! SYS$LOAD_DRIVER ! IOGEN$AUTOCONFIGURE ! IOGEN$GET_PREFIX ! IOGEN$SET_PREFIX ! SYS$FAO ! LIB$GET_VM ! LIB$FREE_VM ! IOGEN$BUILD_CONFIG_TABLE ! IOGEN$DELETE_CONFIG_TABLE ! IOC$INIT_ADP_CONFIG ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN BUILTIN NULLPARAMETER; LOCAL iosb : BLOCK[8,BYTE], iogenwa : REF BLOCK[,BYTE], status; IF NOT (status = smi$common_setup(.smictx, %ASCID'IOGEN', .function, iogenwa)) THEN RETURN .status; IF .smi$ga_context[smictx$v_remote] ! switch to remote node, if necessary THEN BEGIN LOCAL string1_val : INITIAL(0), string2_val : INITIAL(0), show_buf_val : INITIAL(0), items_val : INITIAL(0), bus_list_val : INITIAL(0), dpt_addr_val : INITIAL(0), show_len : INITIAL(0); IF NOT NULLPARAMETER(string1) THEN string1_val = .string1; IF NOT NULLPARAMETER(string2) THEN string2_val = .string2; IF NOT NULLPARAMETER(show_buf) THEN show_buf_val = .show_buf; IF NOT NULLPARAMETER(items) THEN items_val= .items; IF NOT NULLPARAMETER(bus_list) THEN bus_list_val = .bus_list; IF NOT NULLPARAMETER(dpt_addr) THEN dpt_addr_val = .dpt_addr; status = smi$remote_call(.smictx, %ASCID'IOGEN', .function, .flags, .string1_val, .string2_val, .show_buf_val, .items_val, .bus_list_val, .dpt_addr_val, show_len); IF NOT NULLPARAMETER(show_buf) THEN show_buf[dsc$w_length] = .show_len; RETURN .status END; SELECTONE .function OF SET [smi$k_func_auto]: ! IO AUTOCONFIGURE... BEGIN LOCAL buses : INITIAL(0), prefix_buffer : VECTOR[508,BYTE] VOLATILE, prefix_list : $stat_desc(prefix_buffer); BIND log_list = iogenwa[iogenwa$b_data] : LONG; ! Reset the work area data buffer iogenwa[iogenwa$ps_next] = iogenwa[iogenwa$b_data]; log_list = 0; log_where = .iogenwa; ! so that AUTOLOG can find it ! If the bus_list parameter came over as zero length, pass ! IOGEN$AUTOCONFIGURE a null-pointer rather than the empty list ! since an empty list means "do nothing". IF .bus_list[dsc$w_length] NEQ 0 THEN buses = .bus_list[dsc$a_pointer]; IF (status = iogen$get_prefix(prefix_list)) THEN status = iogen$autoconfigure(.flags, prefix_list, .string1, .string2, .buses, autolog); ! Reset the data pointer for showing the autoconfigure log log_where = 0; ! make it disappear again (shudder!) iogenwa[iogenwa$ps_next] = iogenwa[iogenwa$b_data]; RETURN .status END; [smi$k_func_connect]: ! IO CONNECT... BEGIN IF (status = sys$load_driver(iogen$_connect, .string1, .string2, .items, iosb) ) THEN status = .iosb[iosb_w_status]; IF .status THEN .dpt_addr = .iosb[iosb_ps_dpt] ELSE .dpt_addr = 0; RETURN .status END; [smi$k_func_load]: ! IO LOAD... BEGIN IF (status = sys$load_driver(iogen$_load, 0, .string2, 0, iosb) ) THEN status = .iosb[iosb_w_status]; IF .status THEN .dpt_addr = .iosb[iosb_ps_dpt] ELSE .dpt_addr = 0; RETURN .status END; [smi$k_func_reload]: ! IO RELOAD... BEGIN IF (status = sys$load_driver(iogen$_reload, 0, .string2, 0, iosb) ) THEN status = .iosb[iosb_w_status]; IF .status THEN .dpt_addr = .iosb[iosb_ps_dpt] ELSE .dpt_addr = 0; RETURN .status END; [smi$k_func_modify]: ! IO SET ... SELECTONE .flags OF ! SET what? SET [smiiogen$k_set_prefix]: RETURN iogen$set_prefix(.string1); [smiiogen$k_set_exclusion]: RETURN iogen$set_exclusion(.string1); TES; [smi$k_func_show]: ! IO SHOW... BEGIN SELECTONE .flags OF ! SHOW what? SET [smiiogen$k_show_prefix]: ! IO SHOW PREFIX status = iogen$get_prefix(.show_buf); [smiiogen$k_show_exclusion]: ! IO SHOW EXCLUDE status = iogen$get_exclusion(.show_buf); [smiiogen$k_show_device]: ! IO SHOW DEVICE BEGIN BIND data = iogenwa[iogenwa$b_data] : LONG; ! Reset the work area to be empty and the show state to be ! "DPT" iogenwa[iogenwa$l_state] = iogenwa$k_show_dpt; iogenwa[iogenwa$ps_next] = iogenwa[iogenwa$b_data]; data = 0; ! Now collect the data and return the header IF NOT (status = kernel_call(show_dev, .iogenwa)) THEN RETURN .status; status = $fao(%ASCID'!/__Driver________Dev_DDB______CRB______IDB_______Unit__UCB_____', .show_buf, .show_buf) END; [smiiogen$k_show_bus]: BEGIN LOCAL data : REF VECTOR[,LONG] INITIAL(iogenwa[iogenwa$b_data]); ! Now collect the bus data. IF NOT (status = kernel_call(show_bus, .iogenwa)) THEN RETURN .status; ! Reset the stack to be empty and set up the first ADP ! information. iogenwa[iogenwa$ps_sp] = .iogenwa + iogenwa$k_length; read_long(iogenwa[iogenwa$l_adptype]); read_long(iogenwa[iogenwa$l_tr]); read_long(iogenwa[iogenwa$l_bus_type]); iogenwa[iogenwa$ps_next] = .data; iogenwa[iogenwa$l_indent] = 0; status = $fao(%ASCID'!/_Bus__________Node_TR#__Name____________Base CSR__________', .show_buf, .show_buf) END; [smiiogen$k_show_autolog]: status = show_log(.iogenwa, .show_buf); TES; ! Now set the returned length in the optional parameter SHOW_LEN to ! fix up the failure of SYSMAN to properly handle output ! descriptors. IF NOT NULLPARAMETER(show_len) THEN .show_len = .show_buf[dsc$w_length]; RETURN .status END; [smi$k_func_show_next]: ! continuation of IO SHOW... or IO AUTO/LOG BEGIN SELECTONE .flags OF ! continuation of what? SET [smiiogen$k_show_prefix, smiiogen$k_show_exclusion]: RETURN smi$_nomore; [smiiogen$k_show_device]: status = show_dev_next(.iogenwa, .show_buf); [smiiogen$k_show_bus]: status = show_bus_next(.iogenwa, .show_buf); [smiiogen$k_show_autolog]: status = show_log(.iogenwa, .show_buf); TES; ! Now set the returned length in the optional parameter SHOW_LEN to ! fix up the failure of SYSMAN to properly handle output ! descriptors. IF NOT NULLPARAMETER(show_len) THEN .show_len = .show_buf[dsc$w_length]; RETURN .status END; [smi$k_func_config_rebuild]: ! Rebuild of device config tables BEGIN LOCAL config_table; ! Build the config table in user mode - we're ! linking against a form of READ_CONFIG which uses ! RMS, not the primitive file system. IF NOT (status = iogen$build_config_table (-1, config_table)) THEN RETURN .status; ! Now take the config table built by iogen$build_config_table, ! and fill in config tables linked to adp$ps_config_table, as ! long as /VERIFY was *not* specified. This has to be done in ! kernel mode. IF (.flags AND IOGEN$M_AC_VERIFY) NEQ IOGEN$M_AC_VERIFY THEN IF NOT (status = kernel_call(ioc$init_adp_config, .config_table)) THEN RETURN .status; ! Delete the comprehensive config table built by ! iogen$build_config_table. status = iogen$delete_config_table (.config_table); RETURN .status; END; TES; ! If we get here, something is bogus. Return an error. RETURN ss$_badparam END; %SBTTL 'AUTOLOG, capture AUTOCONFIGURE logging' ROUTINE autolog(msg : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine is called as a $PUTMSG action routine when autoconfigure ! logging is enabled. It stores the message away in the IOGENWA (whose ! address is temporarily available in OWN storage as LOG_WHERE). The message ! is stored as a LONGWORD of length, followed by text. Each length LONGWORD ! is naturally aligned. ! ! FORMAL PARAMETERS: ! ! msg - descriptor of message generated by $PUTMSG ! ! IMPLICIT INPUT PARAMETERS: ! ! log_where - address of the IOGEN work area for this thread ! ! IMPLICIT OUTPUT PARAMETERS: ! ! the IOGEN work area is substantially modified ! ! RETURN VALUE: ! ! 0 ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL iogenwa : REF BLOCK[,BYTE] INITIAL (.log_where), buffer : REF VECTOR[,BYTE], next : REF VECTOR[,LONG]; BIND msglen = .iogenwa[iogenwa$ps_next] : LONG; buffer = .iogenwa[iogenwa$ps_next] + 4; msglen = .msg[dsc$w_length]; next = next_longword(.buffer + .msglen); ! If there is room for the message and another length longword after it (to ! act as a terminator), copy the message. Otherwise stomp the message ! length back to 0 (to terminate the list) and drop the message on the ! floor. IF .next LSSU (.iogenwa + iogenwa$k_length) THEN BEGIN CH$MOVE(.msglen, .msg[dsc$a_pointer], .buffer); iogenwa[iogenwa$ps_next] = .next END ELSE next = msglen; .next = 0; ! insure list is terminated RETURN 0 ! suppress output from $PUTMSG END; %SBTTL 'SHOW_DEV, collect data for SHOW DEVICE' ROUTINE show_dev(iogenwa : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine gathers all of the data necessary to execute the SHOW DEVICE ! command. ! ! FORMAL PARAMETERS: ! ! iogenwa - address of the IOGEN object work area ! ! IMPLICIT INPUT PARAMETERS: ! ! the I/O database ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! None ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL data : REF VECTOR[,LONG] INITIAL (iogenwa[iogenwa$b_data]), sb : REF BLOCK[,BYTE] INITIAL (.scs$ar_localsb), dpt : REF BLOCK[,BYTE] INITIAL (.ioc$gl_dptlist), ddb : REF BLOCK[,BYTE], crb : REF BLOCK[,BYTE], vec : REF BLOCK[,BYTE], ucb : REF BLOCK[,BYTE]; ! First, lock the I/O database mutex for read sch$iolockr(.ctl$gl_pcb); ! Now, walk the DPT list, processing each DPT WHILE .dpt NEQA ioc$gl_dptlist DO BEGIN ! Save the DPT name (ASCIC) and pad out to the next longword boundary data = next_longword(CH$MOVE(.dpt[dpt$ib_name_len]+1, dpt[dpt$t_name], .data)); ! Now search the local DDB list for DDBs associated with the current ! driver. For any that are found, store away the DDB, CRB and IDB ! addresses ddb = .sb[sb$l_ddb]; WHILE .ddb NEQA 0 DO ! this loop walks the whole list BEGIN WHILE .ddb NEQA 0 DO ! this loop searches for a DDB/DPT match IF .dpt EQLA .ddb[ddb$ps_dpt] THEN EXITLOOP ELSE ddb = .ddb[ddb$ps_link]; IF .ddb EQLA 0 ! no more DDBs for this driver THEN EXITLOOP; store_long(.ddb); data = next_longword(CH$MOVE(.ddb[ddb$ib_name_len]+1, ddb[ddb$t_name], .data)); IF (ucb = .ddb[ddb$ps_ucb]) NEQA 0 THEN BEGIN crb = .ucb[ucb$l_crb]; vec = crb[crb$l_intd]; store_long(.crb); store_long(.vec[vec$l_idb]) END ELSE BEGIN store_long(0); ! no CRB store_long(0) ! no IDB END; ! Now, loop through all UCBs saving the UCB address and unit # WHILE .ucb NEQA 0 DO BEGIN store_long(.ucb); store_long(.ucb[ucb$w_unit]); ucb = .ucb[ucb$l_link] END; store_long(0); ! no more units on this controller ddb = .ddb[ddb$ps_link] ! continue search on next DDB END; store_long(0); ! no more controllers for this driver dpt = .dpt[dpt$ps_flink] END; ! All done. Unlock the I/O database mutex, terminate the data and leave sch$iounlock(.ctl$gl_pcb); $setipl(newipl = 0); store_long(0); RETURN ss$_normal END; %SBTTL 'SHOW_DEV_NEXT, format the next line of the SHOW DEVICE display' ROUTINE show_dev_next( iogenwa : REF BLOCK[,BYTE], show_buf : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine takes the data that was collected by SHOW_DEV and formats it a ! line at a time. It assumes that IOGENWA$PS_NEXT points to the next set of ! data to process and that IOGENWA$L_STATE reflects the current state. These ! two fields are updated appropriately as SHOW_DEV_NEXT does its work. There ! are 3 possible states with expected table contents as follows: ! ! State: DPT DDB UCB ! -------------------------------------------------------------------------- ! Driver name in counted DDB address UCB address ! ASCII form ! -------------------------------------------------------------------------- ! padding to next longword DDB name in counted unit number ! boundary ASCII form ! -------------------------------------------------------------------------- ! CRB address ! -------------------------------------------------------------------------- ! IDB address ! -------------------------------------------------------------------------- ! ! Finding a "0" for the first value in a state causes the state to move left ! one column in the chart above. Left from DPT state means all data has been ! shown. Non-zero values for the first value in the DPT and DDB states causes ! the state to move one column to the right in the chart above. A non-zero ! UCB address causes SHOW_DEV_NEXT to remain in UCB state. ! ! FORMAL PARAMETERS: ! ! iogenwa - address of the IOGEN object work area ! show_buf - address of the return buffer's descriptor ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SMI$_NOMORE ! return values from SYS$FAO ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL data : REF VECTOR[,LONG] INITIAL (.iogenwa[iogenwa$ps_next]), name : REF VECTOR[,BYTE], ddb, crb, idb, ucb, unit; WHILE 1 DO SELECTONE .iogenwa[iogenwa$l_state] OF SET [iogenwa$k_show_dpt]: BEGIN IF .data[0] EQL 0 THEN RETURN smi$_nomore; name = .data; iogenwa[iogenwa$ps_next] = next_longword(.data + .name[0] + 1); iogenwa[iogenwa$l_state] = iogenwa$k_show_ddb; RETURN $fao(%ASCID'!AC', .show_buf, .show_buf, .name) END; [iogenwa$k_show_ddb]: BEGIN read_long(ddb); IF .ddb NEQA 0 THEN BEGIN name = .data; data = next_longword(.data + .name[0] + 1); read_long(crb); read_long(idb); iogenwa[iogenwa$ps_next] = .data; iogenwa[iogenwa$l_state] = iogenwa$k_show_ucb; RETURN $fao( %ASCID'!16* !AC !XL !XL !XL', .show_buf, .show_buf, .name, .ddb, .crb, .idb) END ELSE iogenwa[iogenwa$l_state] = iogenwa$k_show_dpt END; [iogenwa$k_show_ucb]: BEGIN read_long(ucb); IF .ucb NEQA 0 THEN BEGIN read_long(unit); iogenwa[iogenwa$ps_next] = .data; RETURN $fao( %ASCID'!47* !5UW !XL', .show_buf, .show_buf, .unit, .ucb) END ELSE iogenwa[iogenwa$l_state] = iogenwa$k_show_ddb END; TES; RETURN ss$_abort ! deep sneakers if we get here END; %SBTTL 'SHOW_BUS, gather data for SHOW BUS command' ROUTINE show_bus(iogenwa : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine gathers the data necessary to generate a SHOW BUS display. It ! only expressly looks at the root ADP and all second level ADPs. It assumes ! that the bus arrays associated with all second- and lower- level ADPs will ! have ADP pointers to lower levels where appropriate. ! ! FORMAL PARAMETERS: ! ! iogenwa - address of the IOGEN object work area ! ! IMPLICIT INPUT PARAMETERS: ! ! the I/O data base ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SS$_NORMAL ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL adp : REF BLOCK[,BYTE] INITIAL (.ioc$gl_adplist), data : REF VECTOR[,LONG] INITIAL (iogenwa[iogenwa$b_data]); ! First, after locking the I/O database for read, gather information about ! the root ADP. Don't do stack manipulation so that state is properly ! initialized. sch$iolockr(.ctl$gl_pcb); process_adp(.adp, data, 1); ! Now process each second level ADP. This should end up catching all third ! level and lower ADPs as it goes. adp = .adp[adp$ps_child_adp]; WHILE .adp NEQA 0 DO BEGIN process_adp(.adp, data); adp = .adp[adp$ps_peer_adp] END; ! Now, store a pop that will cause a stack underflow (signalling end of ! data) and we're all done. sch$iounlock(.ctl$gl_pcb); $setipl(newipl = 0); store_long(0); RETURN ss$_normal END; %SBTTL 'PROCESS_ADP, gather data from an ADP and its associated bus array' ROUTINE process_adp( adp : REF BLOCK[,BYTE], datap, nostack) : NOVALUE = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine does the real work of data gathering for SHOW_BUS. ! ! FORMAL PARAMETERS: ! ! adp - address of the ADP whose data is to be gathered ! datap - pointer to the pointer into the OWA ! nostack - optional flag to indicated that stack operators should not be ! emitted ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! None ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL busarrayhead : REF BLOCK[,BYTE] INITIAL (.adp[adp$ps_bus_array]), config_table : REF BLOCK[,BYTE] INITIAL (.adp[adp$ps_config_table]), hw_id_mask_lo : INITIAL(-1), hw_id_mask_hi : INITIAL(0), use_stack : INITIAL(0); BIND busarray = busarrayhead[busarray$q_entry_list] : BLOCKVECTOR[,busarrayentry$k_length,BYTE], data = .datap : REF VECTOR[,LONG]; BUILTIN NULLPARAMETER; ! If the ADP has a config table, use its masks for pushing hardware IDs. ! Otherwise, use the default masks which result in the use of 32-bit ! hardware IDs in NDT translation. IF .config_table NEQA 0 THEN BEGIN hw_id_mask_lo = .config_table[iogen_cfg_tbl$l_hw_id_mask_lo]; hw_id_mask_hi = .config_table[iogen_cfg_tbl$l_hw_id_mask_hi] END; ! If NOSTACK is present and true, don't do a PUSH. Otherwise, insert a ! PUSH, the ADP type, the TR # and the bus type into the buffer. IF NULLPARAMETER(nostack) THEN BEGIN store_long(-1); use_stack = 1; END ELSE IF NOT .nostack THEN BEGIN store_long(-1); use_stack = 1; END; store_long(.adp[adp$l_adptype]); store_long(.adp[adp$l_tr]); store_long(.busarrayhead[busarray$l_bus_type]); ! Now, walk the bus array, saving information as found and recursing to ! lower level ADPs as found. INCR i FROM 0 TO .busarrayhead[busarray$l_bus_node_cnt] - 1 DO IF .busarray[.i, busarray$l_hw_id] NEQ 0 THEN IF (.busarray[.i, busarray$ps_adp] NEQA 0) AND .use_stack THEN process_adp(.busarray[.i, busarray$ps_adp], data) ELSE BEGIN BIND csr = busarray[.i, busarray$q_csr] : VECTOR[,LONG]; store_long(.busarray[.i, busarray$l_hw_id] AND .hw_id_mask_lo); store_long(.busarray[.i, busarray$l_hw_id_hi] AND .hw_id_mask_hi); store_long(.busarray[.i, busarray$l_node_number]); store_long(.csr[0]); store_long(.csr[1]) END; ! Now, if we're doing stack operations, store a POP. IF NULLPARAMETER(nostack) THEN BEGIN store_long(0) END ELSE IF NOT .nostack THEN BEGIN store_long(0) END END; %SBTTL 'SHOW_BUS_NEXT, format the next line of output for SHOW BUS' ROUTINE show_bus_next( iogenwa : REF BLOCK[,BYTE], show_buf : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This module formats the data gathered by SHOW_BUS for display, one line at a ! time. It assumes that IOGENWA$PS_SP, IOGENWA$PS_NEXT, IOGENWA$L_ADPTYPE, ! IOGENWA$L_TR, IOGENWA$L_BUS_TYPE and IOGENWA$L_INDENT are properly set up. ! All NDT (HW ID) values are processed for display in the context of the ! current ADPTYPE, TR and BUS_TYPE. NDT values of 0 and -1 are expressly ! disallowed in VMS so these values are used to represent POP and PUSH ! operators, respectively. The next datum to be processed is assumed to be an ! NDT value (or one of these psuedo-operators). ! ! A PUSH pseudo-op (-1) causes the current context to be stored on a stack at ! the end of the object work area, a new context to be read from the next ! three longwords (the order is assumed as ADPTYPE, TR, BUS_TYPE) and the ! indent level to be incremented. ! ! A POP pseudo-op (0) causes the current context to be overwritten by that at ! the top of the stack and the indent level to be decremented. A stack ! underflow is the normal indication that all data has been processed. ! ! Any other NDT value is assumed to be valid and is further assumed to be ! followed by 3 additional longwords containing, in order, the node number, ! the low longword of the base CSR address and the high longword of the base ! CSR address. ! ! FORMAL PARAMETERS: ! ! iogenwa - address of the IOGEN object work area ! show_buf - address of the output buffer's descriptor ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SMI$_NOMORE ! any return value from SYS$FAO ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL data : REF VECTOR[,LONG] INITIAL(.iogenwa[iogenwa$ps_next]), sp : REF BLOCK[,BYTE] INITIAL(.iogenwa[iogenwa$ps_sp]), adp_name, ndt_name, ndt_lo, ndt_hi, node, csr_lo, csr_hi; WHILE 1 DO BEGIN read_long(ndt_lo); SELECTONE .ndt_lo OF SET [-1]: ! push to a new ADP BEGIN sp = .sp - sbstk$k_length; sp[sbstk$l_adptype] = .iogenwa[iogenwa$l_adptype]; sp[sbstk$l_tr] = .iogenwa[iogenwa$l_tr]; sp[sbstk$l_bus_type] = .iogenwa[iogenwa$l_bus_type]; iogenwa[iogenwa$ps_sp] = .sp; ! Push is complete. Now move to new ADP info. read_long(iogenwa[iogenwa$l_adptype]); read_long(iogenwa[iogenwa$l_tr]); read_long(iogenwa[iogenwa$l_bus_type]); iogenwa[iogenwa$l_indent] = .iogenwa[iogenwa$l_indent] + 1 END; [0]: ! pop to previous ADP (or exit) BEGIN IF .sp EQLA .iogenwa + iogenwa$k_length THEN RETURN smi$_nomore; ! stack underflow indicates all ! done iogenwa[iogenwa$l_adptype] = .sp[sbstk$l_adptype]; iogenwa[iogenwa$l_tr] = .sp[sbstk$l_tr]; iogenwa[iogenwa$l_bus_type] = .sp[sbstk$l_bus_type]; iogenwa[iogenwa$ps_sp] = sp = .sp + sbstk$k_length; iogenwa[iogenwa$l_indent] = .iogenwa[iogenwa$l_indent] - 1 END; [OTHERWISE]: BEGIN adp_name = translate_adptype(.iogenwa[iogenwa$l_adptype]); read_long(ndt_hi); ndt_name = translate_hw_id(.ndt_lo, .ndt_hi, .iogenwa[iogenwa$l_bus_type]); read_long(node); read_long(csr_lo); read_long(csr_hi); ! Data is collected and ready for $FAO. Write the data pointer ! back to the work area for the next pass. Then do the $FAO ! and return. iogenwa[iogenwa$ps_next] = .data; RETURN $fao( %ASCID'!#* !13 !4 !4 !14 !XL!XL', .show_buf, .show_buf, .iogenwa[iogenwa$l_indent] * 2, .adp_name, .node, .iogenwa[iogenwa$l_tr], .ndt_name, .csr_hi, .csr_lo) END; TES END; RETURN ss$_abort ! should never ever get here END; %SBTTL 'SHOW_LOG, dump the next line from AUTOCONFIGURE logging' ROUTINE show_log( iogenwa : REF BLOCK[,BYTE], show_buf : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine returns the next line of AUTOCONFIGURE logging output. A ! length of 0 terminates operation. ! ! FORMAL PARAMETERS: ! ! iogenwa - address of the IOGEN object work area ! show_buf - address of the output buffer's descriptor ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! SMI$_NOMORE ! SS$_NORMAL ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN LOCAL data : REF VECTOR[,LONG] INITIAL(.iogenwa[iogenwa$ps_next]), msglen; read_long(msglen); IF .msglen EQL 0 THEN RETURN smi$_nomore; ! all done CH$MOVE(.msglen, .data, .show_buf[dsc$a_pointer]); show_buf[dsc$w_length] = .msglen; iogenwa[iogenwa$ps_next] = next_longword(.data + .msglen); RETURN ss$_normal END; END ! End of module ELUDOM