patch-2.4.21 linux-2.4.21/arch/ia64/sn/io/sn1/l1_command.c

Next file: linux-2.4.21/arch/ia64/sn/io/sn1/mem_refcnt.c
Previous file: linux-2.4.21/arch/ia64/sn/io/sn1/l1.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/arch/ia64/sn/io/sn1/l1_command.c linux-2.4.21/arch/ia64/sn/io/sn1/l1_command.c
@@ -0,0 +1,1409 @@
+/* $Id: l1_command.c,v 1.2 2002/11/21 17:51:57 jh Exp $
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2002 Silicon Graphics, Inc. All rights reserved.
+ */ 
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/io.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/router.h>
+#include <asm/sn/module.h>
+#include <asm/sn/ksys/l1.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/clksupport.h>
+#include <asm/sn/sn_cpuid.h>
+
+#define ELSC_TIMEOUT	1000000		/* ELSC response timeout (usec) */
+#define LOCK_TIMEOUT	5000000		/* Hub lock timeout (usec) */
+
+#define LD(x)		(*(volatile uint64_t *)(x))
+#define SD(x, v)	(LD(x) = (uint64_t) (v))
+
+#define hub_cpu_get()	0
+
+#define LBYTE(caddr)	(*(char *) caddr)
+
+extern char *bcopy(const char * src, char * dest, int count);
+
+#define LDEBUG		0
+
+/*
+ * ELSC data is in NVRAM page 7 at the following offsets.
+ */
+
+#define NVRAM_MAGIC_AD	0x700		/* magic number used for init */
+#define NVRAM_PASS_WD	0x701		/* password (4 bytes in length) */
+#define NVRAM_DBG1	0x705		/* virtual XOR debug switches */
+#define NVRAM_DBG2	0x706		/* physical XOR debug switches */
+#define NVRAM_CFG	0x707		/* ELSC Configuration info */
+#define NVRAM_MODULE	0x708		/* system module number */
+#define NVRAM_BIST_FLG	0x709		/* BIST flags (2 bits per nodeboard) */
+#define NVRAM_PARTITION 0x70a		/* module's partition id */
+#define	NVRAM_DOMAIN	0x70b		/* module's domain id */
+#define	NVRAM_CLUSTER	0x70c		/* module's cluster id */
+#define	NVRAM_CELL	0x70d		/* module's cellid */
+
+#define NVRAM_MAGIC_NO	0x37		/* value of magic number */
+#define NVRAM_SIZE	16		/* 16 bytes in nvram */
+
+/*
+ * Declare a static ELSC NVRAM buffer to hold all data read from
+ * and written to NVRAM.  This nvram "cache" will be used only during the
+ * IP27prom execution.
+ */
+static char elsc_nvram_buffer[NVRAM_SIZE];
+
+#define SC_COMMAND sc_command
+
+/*
+ * elsc_init
+ *
+ *   Initialize ELSC structure
+ */
+
+void elsc_init(elsc_t *e, nasid_t nasid)
+{
+    sc_init((l1sc_t *)e, nasid, BRL1_LOCALHUB_UART);
+}
+
+
+/*
+ * elsc_errmsg
+ *
+ *   Given a negative error code,
+ *   returns a corresponding static error string.
+ */
+
+char *elsc_errmsg(int code)
+{
+    switch (code) {
+    case ELSC_ERROR_CMD_SEND:
+	return "Command send error";
+    case ELSC_ERROR_CMD_CHECKSUM:
+	return "Command packet checksum error";
+    case ELSC_ERROR_CMD_UNKNOWN:
+	return "Unknown command";
+    case ELSC_ERROR_CMD_ARGS:
+	return "Invalid command argument(s)";
+    case ELSC_ERROR_CMD_PERM:
+	return "Permission denied";
+    case ELSC_ERROR_RESP_TIMEOUT:
+	return "System controller response timeout";
+    case ELSC_ERROR_RESP_CHECKSUM:
+	return "Response packet checksum error";
+    case ELSC_ERROR_RESP_FORMAT:
+	return "Response format error";
+    case ELSC_ERROR_RESP_DIR:
+	return "Response direction error";
+    case ELSC_ERROR_MSG_LOST:
+	return "Message lost because queue is full";
+    case ELSC_ERROR_LOCK_TIMEOUT:
+	return "Timed out getting ELSC lock";
+    case ELSC_ERROR_DATA_SEND:
+	return "Error sending data";
+    case ELSC_ERROR_NIC:
+	return "NIC protocol error";
+    case ELSC_ERROR_NVMAGIC:
+	return "Bad magic number in NVRAM";
+    case ELSC_ERROR_MODULE:
+	return "Module location protocol error";
+    default:
+	return "Unknown error";
+    }
+}
+
+/*
+ * elsc_nvram_init
+ *
+ *   Initializes reads and writes to NVRAM.  This will perform a single
+ *   read to NVRAM, getting all data at once.  When the PROM tries to
+ *   read NVRAM, it returns the data from the buffer being read.  If the
+ *   PROM tries to write out to NVRAM, the write is done, and the internal
+ *   buffer is updated.
+ */
+
+void elsc_nvram_init(nasid_t nasid, uchar_t *elsc_nvram_data)
+{
+    /* This might require implementation of multiple-packet request/responses
+     * if it's to provide the same behavior that was available in SN0.
+     */
+    nasid = nasid;
+    elsc_nvram_data = elsc_nvram_data;
+}
+
+/*
+ * elsc_nvram_copy
+ *
+ *   Copies the content of a buffer into the static buffer in this library.
+ */
+
+void elsc_nvram_copy(uchar_t *elsc_nvram_data)
+{
+    memcpy(elsc_nvram_buffer, elsc_nvram_data, NVRAM_SIZE);
+}
+
+/*
+ * elsc_nvram_write
+ *
+ *   Copies bytes from 'buf' into NVRAM, starting at NVRAM address
+ *   'addr' which must be between 0 and 2047.
+ *
+ *   If 'len' is non-negative, the routine copies 'len' bytes.
+ *
+ *   If 'len' is negative, the routine treats the data as a string and
+ *   copies bytes up to and including a NUL-terminating zero, but not
+ *   to exceed '-len' bytes.
+ */
+
+int elsc_nvram_write(elsc_t *e, int addr, char *buf, int len)
+{
+    /* Here again, we might need to work out the details of a
+     * multiple-packet protocol.
+     */
+
+    /* For now, pretend it worked. */
+    e = e;
+    addr = addr;
+    buf = buf;
+    return (len < 0 ? -len : len);
+}
+
+/*
+ * elsc_nvram_read
+ *
+ *   Copies bytes from NVRAM into 'buf', starting at NVRAM address
+ *   'addr' which must be between 0 and 2047.
+ *
+ *   If 'len' is non-negative, the routine copies 'len' bytes.
+ *
+ *   If 'len' is negative, the routine treats the data as a string and
+ *   copies bytes up to and including a NUL-terminating zero, but not
+ *   to exceed '-len' bytes.  NOTE:  This method is no longer supported.
+ *   It was never used in the first place.
+ */
+
+int elsc_nvram_read(elsc_t *e, int addr, char *buf, int len)
+{
+    /* multiple packets? */
+    e = e;
+    addr = addr;
+    buf = buf;
+    len = len;
+    return -1;
+}
+
+
+/*
+ * Command Set
+ */
+
+int elsc_version(elsc_t *e, char *result)
+{
+    char	msg[BRL1_QSIZE];
+    int		len;    /* length of message being sent */
+    int		subch;  /* system controller subchannel used */
+    int		major,  /* major rev number */
+	        minor,  /* minor rev number */
+                bugfix; /* bugfix rev number */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL );
+
+    if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_FW_REV, 0 )) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND( (l1sc_t *)e, subch, msg, msg, &len ) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( (l1sc_t *)e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 6, L1_ARG_INT, &major,
+			   L1_ARG_INT, &minor, L1_ARG_INT, &bugfix )
+	< 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    sprintf( result, "%d.%d.%d", major, minor, bugfix );
+
+    return 0;
+}
+
+int elsc_debug_set(elsc_t *e, u_char byte1, u_char byte2)
+{
+    /* shush compiler */
+    e = e;
+    byte1 = byte1;
+    byte2 = byte2;
+
+    /* fill in a buffer with the opcode & params; call sc_command */
+
+    return 0;
+}
+
+int elsc_debug_get(elsc_t *e, u_char *byte1, u_char *byte2)
+{
+    char	msg[BRL1_QSIZE];
+    int		subch;  /* system controller subchannel used */
+    int		dbg_sw; /* holds debug switch settings */
+    int		len;	/* number of msg buffer bytes used */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL )) < 0 ) {
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_RDBG, 0 ) ) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( (l1sc_t *)e, subch, msg, msg, &len ) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( (l1sc_t *)e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 2, L1_ARG_INT, &dbg_sw ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    /* copy out debug switch settings (last two bytes of the
+     * integer response)
+     */
+    *byte1 = ((dbg_sw >> 8) & 0xFF);
+    *byte2 = (dbg_sw & 0xFF);
+
+    return 0;
+}
+
+
+/*
+ * elsc_rack_bay_get fills in the two int * arguments with the
+ * rack number and bay number of the L1 being addressed
+ */
+int elsc_rack_bay_get(elsc_t *e, uint *rack, uint *bay)
+{
+    char msg[BRL1_QSIZE];	/* L1 request/response info */
+    int subch;			/* system controller subchannel used */
+    int len;			/* length of message */
+    uint32_t	buf32;		/* used to copy 32-bit rack/bay out of msg */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL )) < 0 ) {
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_RRACK, 0 )) < 0 ) 
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+
+    /* send the request to the L1 */
+    if( sc_command( (l1sc_t *)e, subch, msg, msg, &len ) ) {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close(e, subch);
+
+    /* check response */
+    if( sc_interpret_resp( msg, 2, L1_ARG_INT, &buf32 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    /* extract rack/bay info
+     *
+     * note that the 32-bit value returned by the L1 actually
+     * only uses the low-order sixteen bits for rack and bay
+     * information.  A "normal" L1 address puts rack and bay
+     * information in bit positions 12 through 28.  So if
+     * we initially shift the value returned 12 bits to the left,
+     * we can use the L1 addressing #define's to extract the
+     * values we need (see ksys/l1.h for a complete list of the
+     * various fields of an L1 address).
+     */
+    buf32 <<= L1_ADDR_BAY_SHFT;
+
+    *rack = (buf32 & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
+    *bay = (buf32 & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
+
+    return 0;
+}
+
+
+/* elsc_rack_bay_type_get fills in the three int * arguments with the
+ * rack number, bay number and brick type of the L1 being addressed.  Note
+ * that if the L1 operation fails and this function returns an error value, 
+ * garbage may be written to brick_type.
+ */
+int elsc_rack_bay_type_get( l1sc_t *sc, uint *rack, 
+			       uint *bay, uint *brick_type )
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+    uint32_t buf32;	        /* used to copy 32-bit rack & bay out of msg */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( sc, L1_ADDR_LOCAL )) < 0 ) {
+	return ELSC_ERROR_CMD_SEND;
+    }
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_RRBT, 0 )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND( sc, subch, msg, msg, &len ) ) {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 4, L1_ARG_INT, &buf32, 
+			           L1_ARG_INT, brick_type ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    /* extract rack/bay info
+     *
+     * note that the 32-bit value returned by the L1 actually
+     * only uses the low-order sixteen bits for rack and bay
+     * information.  A "normal" L1 address puts rack and bay
+     * information in bit positions 12 through 28.  So if
+     * we initially shift the value returned 12 bits to the left,
+     * we can use the L1 addressing #define's to extract the
+     * values we need (see ksys/l1.h for a complete list of the
+     * various fields of an L1 address).
+     */
+    buf32 <<= L1_ADDR_BAY_SHFT;
+
+    *rack = (buf32 & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
+    *bay = (buf32 & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
+
+    /* convert brick_type to lower case */
+    *brick_type = *brick_type - 'A' + 'a';
+
+    return 0;
+}
+
+
+int elsc_module_get(elsc_t *e)
+{
+    extern char brick_types[];
+    uint rnum, rack, bay, bricktype, t;
+    int ret;
+
+    /* construct module ID from rack and slot info */
+
+    if ((ret = elsc_rack_bay_type_get(e, &rnum, &bay, &bricktype)) < 0) {
+	return ret;
+    }
+
+    /* report unset location info. with a special, otherwise invalid modid */
+    if (rnum == 0 && bay == 0)
+	return MODULE_NOT_SET;
+
+    if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
+	return ELSC_ERROR_MODULE;
+
+    /* Build a moduleid_t-compatible rack number */
+
+    rack = 0;		
+    t = rnum / 100;		/* rack class (CPU/IO) */
+    if (t > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
+	return ELSC_ERROR_MODULE;
+    RACK_ADD_CLASS(rack, t);
+    rnum %= 100;
+
+    t = rnum / 10;		/* rack group */
+    if (t > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
+	return ELSC_ERROR_MODULE;
+    RACK_ADD_GROUP(rack, t);
+
+    t = rnum % 10;		/* rack number (one-based) */
+    if (t-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
+	return ELSC_ERROR_MODULE;
+    RACK_ADD_NUM(rack, t);
+
+    for( t = 0; t < MAX_BRICK_TYPES; t++ ) {
+	if( brick_types[t] == bricktype )
+	    return RBT_TO_MODULE(rack, bay, t);
+    }
+    
+    return ELSC_ERROR_MODULE;
+}
+
+int elsc_partition_set(elsc_t *e, int partition)
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( e, L1_ADDR_LOCAL )) < 0 ) {
+	return ELSC_ERROR_CMD_SEND;
+    }
+
+    if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_PARTITION_SET, 2,
+				 L1_ARG_INT, partition )) < 0 )
+    {
+	
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( e, subch, msg, msg, &len ) ) {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+    
+    return( 0 );
+}
+
+int elsc_partition_get(elsc_t *e)
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+    uint32_t partition_id;    /* used to copy partition id out of msg */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( e, L1_ADDR_LOCAL )) < 0 ) {
+	return ELSC_ERROR_CMD_SEND;
+    }
+
+    if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_PARTITION_GET, 0 )) < 0 )
+
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( e, subch, msg, msg, &len ) ) {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 2, L1_ARG_INT, &partition_id ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+    
+    return( partition_id );
+}
+
+
+/*
+ * elsc_cons_subch selects the "active" console subchannel for this node
+ * (i.e., the one that will currently receive input)
+ */
+int elsc_cons_subch(elsc_t *e, uint ch)
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( e, L1_ADDR_LOCAL );
+    
+    if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_CONS_SUBCH, 2,
+				 L1_ARG_INT, ch)) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND( e, subch, msg, msg, &len ) ) {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    return 0;
+}
+
+
+/*
+ * elsc_cons_node should only be executed by one node.  It declares to
+ * the system controller that the node from which it is called will be
+ * the owner of the system console.
+ */
+int elsc_cons_node(elsc_t *e)
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( e, L1_ADDR_LOCAL );
+    
+    if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_CONS_NODE, 0 )) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND( e, subch, msg, msg, &len ) ) {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    return 0;
+}
+    
+
+/* elsc_display_line writes up to 12 characters to either the top or bottom
+ * line of the L1 display.  line points to a buffer containing the message
+ * to be displayed.  The zero-based line number is specified by lnum (so
+ * lnum == 0 specifies the top line and lnum == 1 specifies the bottom).
+ * Lines longer than 12 characters, or line numbers not less than
+ * L1_DISPLAY_LINES, cause elsc_display_line to return an error.
+ */
+int elsc_display_line(elsc_t *e, char *line, int lnum)
+{
+    char	msg[BRL1_QSIZE];
+    int		subch;  /* system controller subchannel used */
+    int		len;	/* number of msg buffer bytes used */
+
+    /* argument sanity checking */
+    if( !(lnum < L1_DISPLAY_LINES) )
+	return( ELSC_ERROR_CMD_ARGS );
+    if( !(strlen( line ) <= L1_DISPLAY_LINE_LENGTH) )
+	return( ELSC_ERROR_CMD_ARGS );
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL );
+
+    if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 (L1_REQ_DISP1+lnum), 2,
+				 L1_ARG_ASCII, line )) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND( (l1sc_t *)e, subch, msg, msg, &len ) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( (l1sc_t *)e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    return 0;
+}
+
+
+/* elsc_display_mesg silently drops message characters beyond the 12th.
+ */
+int elsc_display_mesg(elsc_t *e, char *chr)
+{
+
+    char line[L1_DISPLAY_LINE_LENGTH+1];
+    int numlines, i;
+    int result;
+
+    numlines = (strlen( chr ) + L1_DISPLAY_LINE_LENGTH - 1) /
+	L1_DISPLAY_LINE_LENGTH;
+
+    if( numlines > L1_DISPLAY_LINES )
+	numlines = L1_DISPLAY_LINES;
+
+    for( i = 0; i < numlines; i++ )
+    {
+	strncpy( line, chr, L1_DISPLAY_LINE_LENGTH );
+	line[L1_DISPLAY_LINE_LENGTH] = '\0';
+
+	/* generally we want to leave the first line of the L1 display
+	 * alone (so the L1 can manipulate it).  If you need to be able
+	 * to display to both lines (for debugging purposes), define
+	 * L1_DISP_2LINES in irix/kern/ksys/l1.h, or add -DL1_DISP_2LINES
+	 * to your 'defs file.
+	 */
+#if defined(L1_DISP_2LINES)
+	if( (result = elsc_display_line( e, line, i )) < 0 )
+#else
+	if( (result = elsc_display_line( e, line, i+1 )) < 0 )
+#endif
+
+	    return result;
+
+	chr += L1_DISPLAY_LINE_LENGTH;
+    }
+    
+    return 0;
+}
+
+
+int elsc_password_set(elsc_t *e, char *password)
+{
+    /* shush compiler */
+    e = e;
+    password = password;
+
+    /* fill in buffer with the opcode & params; call elsc_command */
+
+    return 0;
+}
+
+int elsc_password_get(elsc_t *e, char *password)
+{
+    /* shush compiler */
+    e = e;
+    password = password;
+
+    /* fill in buffer with the opcode & params; call elsc_command */
+
+    return 0;
+}
+
+
+/*
+ * sc_portspeed_get
+ *
+ * retrieve the current portspeed setting for the bedrock II
+ */
+int sc_portspeed_get(l1sc_t *sc)
+{
+    char	msg[BRL1_QSIZE];
+    int         len;    /* length of message being sent */
+    int         subch;  /* system controller subchannel used */
+    int		portspeed_a, portspeed_b;
+			/* ioport clock rates */
+
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( sc, L1_ADDR_LOCAL );
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+                                 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_PORTSPEED,
+				 0 )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+    
+    /* send the request to the L1 */
+    if( sc_command( sc, subch, msg, msg, &len ) < 0 )
+    {
+        sc_close( sc, subch );
+        return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 4, 
+			   L1_ARG_INT, &portspeed_a,
+			   L1_ARG_INT, &portspeed_b ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    /* for the c-brick, we ignore the portspeed_b value */
+    return (portspeed_a ? 600 : 400);
+}
+
+/*
+ * elsc_power_query
+ *
+ *   To be used after system reset, this command returns 1 if the reset
+ *   was the result of a power-on, 0 otherwise.
+ *
+ *   The power query status is cleared to 0 after it is read.
+ */
+
+int elsc_power_query(elsc_t *e)
+{
+    e = e; /* shush the compiler */
+
+    /* fill in buffer with the opcode & params; call elsc_command */
+
+    return 1;
+}
+
+int elsc_rpwr_query(elsc_t *e, int is_master)
+{
+    /* shush the compiler */
+    e = e;
+    is_master = is_master;
+
+    /* fill in buffer with the opcode & params; call elsc_command */
+
+    return 0;
+} 
+
+/*
+ * elsc_power_down
+ *
+ *   Sets up system to shut down in "sec" seconds (or modifies the
+ *   shutdown time if one is already in effect).  Use 0 to power
+ *   down immediately.
+ */
+
+int elsc_power_down(elsc_t *e, int sec)
+{
+    /* shush compiler */
+    e = e;
+    sec = sec;
+
+    /* fill in buffer with the opcode & params; call elsc_command */
+
+    return 0;
+}
+
+
+int elsc_system_reset(elsc_t *e)
+{
+    char	msg[BRL1_QSIZE];
+    int		subch;  /* system controller subchannel used */
+    int		len;	/* number of msg buffer bytes used */
+    int		result;
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( e, L1_ADDR_LOCAL )) < 0 ) {
+	return ELSC_ERROR_CMD_SEND;
+    }
+
+    if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_RESET, 0 )) < 0 )
+    {
+	sc_close( e, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( (result = sc_command( e, subch, msg, msg, &len )) ) {
+	sc_close( e, subch );
+	if( result == SC_NMSG ) {
+	    /* timeout is OK.  We've sent the reset.  Now it's just
+	     * a matter of time...
+	     */
+	    return( 0 );
+	}
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( e, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    return 0;
+}
+
+
+int elsc_power_cycle(elsc_t *e)
+{
+    /* shush compiler */
+    e = e;
+
+    /* fill in buffer with the opcode & params; call sc_command */
+
+    return 0;
+}
+
+
+int _elsc_hbt(elsc_t *e, int ival, int rdly)
+{
+    e = e;
+    ival = ival;
+    rdly = rdly;
+
+    /* fill in buffer with the opcode & params; call elsc_command */
+
+    return 0;
+}
+
+
+/* send a command string to an L1 */
+int sc_command_interp( l1sc_t *sc, l1addr_t compt, l1addr_t rack, l1addr_t bay,
+		       char *cmd )
+{
+    char        msg[BRL1_QSIZE];
+    int         len;    /* length of message being sent */
+    int         subch;  /* system controller subchannel used */
+    l1addr_t	target; /* target system controller for command */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+
+    L1_BUILD_ADDR( &target, compt, rack, bay, 0 );
+    subch = sc_open( sc, target );
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_CMD, L1_REQ_EXEC_CMD, 2,
+				 L1_ARG_ASCII, cmd )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+		   
+    /* send the request to the L1 */
+    if( SC_COMMAND( sc, subch, msg, msg, &len ) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+    
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    return 0;
+}
+
+/*
+ * sc_power_down
+ *
+ * Shuts down the c-brick associated with sc, and any attached I/O bricks
+ * or other c-bricks (won't go through r-bricks).
+ */
+
+int sc_power_down(l1sc_t *sc)
+{
+    return sc_command_interp( sc, L1_ADDR_TYPE_L1, L1_ADDR_RACK_LOCAL, 
+			      L1_ADDR_BAY_LOCAL, "* pwr d" );
+}
+
+
+/*
+ * sc_power_down_all
+ *
+ * Works similarly to sc_power_down, except that the request is sent to the
+ * closest L2 and EVERYBODY gets turned off.
+ */
+
+int sc_power_down_all(l1sc_t *sc)
+{
+    if( nodepda->num_routers > 0 ) {
+	return sc_command_interp( sc, L1_ADDR_TYPE_L2, L1_ADDR_RACK_LOCAL, 
+				  L1_ADDR_BAY_LOCAL, "* pwr d" );
+    }
+    else {
+	return sc_power_down( sc );
+    }
+}
+
+
+/*
+ * iobrick routines
+ */
+
+/* iobrick_rack_bay_type_get fills in the three int * arguments with the
+ * rack number, bay number and brick type of the L1 being addressed.  Note
+ * that if the L1 operation fails and this function returns an error value, 
+ * garbage may be written to brick_type.
+ */
+int iobrick_rack_bay_type_get( l1sc_t *sc, uint *rack, 
+			       uint *bay, uint *brick_type )
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+    uint32_t buf32;	        /* used to copy 32-bit rack & bay out of msg */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( sc, L1_ADDR_LOCALIO )) < 0 ) {
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_RRBT, 0 )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( sc, subch, msg, msg, &len ) ) {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 4, L1_ARG_INT, &buf32, 
+			           L1_ARG_INT, brick_type ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    /* extract rack/bay info
+     *
+     * note that the 32-bit value returned by the L1 actually
+     * only uses the low-order sixteen bits for rack and bay
+     * information.  A "normal" L1 address puts rack and bay
+     * information in bit positions 12 through 28.  So if
+     * we initially shift the value returned 12 bits to the left,
+     * we can use the L1 addressing #define's to extract the
+     * values we need (see ksys/l1.h for a complete list of the
+     * various fields of an L1 address).
+     */
+    buf32 <<= L1_ADDR_BAY_SHFT;
+
+    *rack = (buf32 & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
+    *bay = (buf32 & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
+
+    return 0;
+}
+
+
+int iobrick_module_get(l1sc_t *sc)
+{
+    uint rnum, rack, bay, brick_type, t;
+    int ret;
+
+    /* construct module ID from rack and slot info */
+
+    if ((ret = iobrick_rack_bay_type_get(sc, &rnum, &bay, &brick_type)) < 0)
+        return ret;
+
+    if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
+        return ELSC_ERROR_MODULE;
+
+    /* Build a moduleid_t-compatible rack number */
+
+    rack = 0;           
+    t = rnum / 100;             /* rack class (CPU/IO) */
+    if (t > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
+        return ELSC_ERROR_MODULE;
+    RACK_ADD_CLASS(rack, t);
+    rnum %= 100;
+
+    t = rnum / 10;              /* rack group */
+    if (t > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
+        return ELSC_ERROR_MODULE;
+    RACK_ADD_GROUP(rack, t);
+
+    t = rnum % 10;              /* rack number (one-based) */
+    if (t-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
+        return ELSC_ERROR_MODULE;
+    RACK_ADD_NUM(rack, t);
+
+    switch( brick_type ) {
+      case 'I': 
+	brick_type = MODULE_IBRICK; break;
+      case 'P':
+	brick_type = MODULE_PBRICK; break;
+      case 'X':
+	brick_type = MODULE_XBRICK; break;
+    }
+
+    ret = RBT_TO_MODULE(rack, bay, brick_type);
+
+    return ret;
+}
+
+/*
+ * iobrick_module_get_nasid() returns a module_id which has the brick
+ * type encoded in bits 15-12, but this is not the true brick type...
+ * The module_id returned by iobrick_module_get_nasid() is modified
+ * to make a PEBRICKs & PXBRICKs look like a PBRICK.  So this routine
+ * iobrick_type_get_nasid() returns the true unmodified brick type.
+ */
+int
+iobrick_type_get_nasid(nasid_t nasid)
+{
+    l1sc_t *sc = get_elsc();
+    elsc_t tmp_sc;
+    uint rack, bay, type;
+    int t, ret;
+
+#ifdef PIC_LATER
+    if (PEBRICK_NODE(nasid)) {
+        if (peer_iobrick_rack_bay_get(nasid, &rack, &bay)) {
+            printf("Could not read rack and bay location "
+                   "of PEBrick at nasid %d\n", nasid);
+        }
+        if ((ret = peer_iobrick_type_get(sc, rack, bay, &type)) < 0)
+            return ret;
+    }
+    else
+#endif	/* PIC_LATER */
+    if (nasid != get_nasid()) { /* get the io_moduleid from remote node */
+        elsc_init(&tmp_sc, nasid);
+        if ((ret = iobrick_rack_bay_type_get(&tmp_sc, &rack, &bay, &type)) < 0)
+            return ret;
+    }
+    else {
+        if ((ret = iobrick_rack_bay_type_get(sc, &rack, &bay, &type)) < 0)
+            return ret;
+    }
+
+    /*
+     * Some brick_types need special treatment.  NOTE: This switch is
+     * duplicated in iobrick_module_get(),  so if you change this switch
+     * you must also change it there.
+     */
+    switch (type)
+    {
+    case L1_BRICKTYPE_IP45:
+        /* treat speedo2 like Ibrick for moduleid purposes */
+        type = L1_BRICKTYPE_I;
+        break;
+    case L1_BRICKTYPE_X2:
+        /* give X2 bricks the same moduleid as earlier models */
+        type = L1_BRICKTYPE_X;
+        break;
+    }
+
+    /* convert brick_type to lower case */
+    if ((type >= 'A') && (type <= 'Z'))
+        type = type - 'A' + 'a';
+
+    /* convert to a module.h brick type */
+    for( t = 0; t < MAX_BRICK_TYPES; t++ ) {
+        if( brick_types[t] == type )
+            return t;
+    }
+
+    return -1;    /* unknown brick */
+}
+
+int iobrick_module_get_nasid(nasid_t nasid)
+{
+    int io_moduleid;
+    l1sc_t *sc = get_elsc();
+    elsc_t tmp_sc;
+
+#ifdef PIC_LATER
+    uint rack, bay;
+
+    if (PEBRICK_NODE(nasid)) {
+        if (peer_iobrick_rack_bay_get(nasid, &rack, &bay)) {
+            printf("Could not read rack and bay location "
+                   "of PEBrick at nasid %d\n", nasid);
+        }
+
+        io_moduleid = peer_iobrick_module_get(sc, rack, bay);
+    }
+    else
+#endif	/* PIC_LATER */
+    if (nasid != get_nasid()) { /* get the io_moduleid from remote node */
+        elsc_init(&tmp_sc, nasid);
+
+        io_moduleid = iobrick_module_get(&tmp_sc);
+    }
+    else {
+        io_moduleid = iobrick_module_get(sc);
+    }
+
+    return io_moduleid;
+}
+
+
+/* iobrick_get_sys_snum asks the attached iobrick for the system
+ * serial number.  This function will only be relevant to the master
+ * cbrick (the one attached to the bootmaster ibrick); other nodes
+ * may call the function, but the value returned to the master node
+ * will be the one used as the system serial number by the kernel.
+ */
+
+int
+iobrick_get_sys_snum( l1sc_t *sc, char *snum_str )
+{
+    char msg[BRL1_QSIZE];       /* L1 request/response info */
+    int subch;                  /* system controller subchannel used */
+    int len;                    /* length of message */
+    
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    if( (subch = sc_open( sc, L1_ADDR_LOCALIO )) < 0 ) {
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_SYS_SERIAL, 0 )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( sc, subch, msg, msg, &len ) ) {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+
+    /* check response */
+    return( sc_interpret_resp( msg, 2, L1_ARG_ASCII, snum_str ) );
+}
+
+
+/*
+ * The following functions apply (or cut off) power to the specified
+ * pci bus or slot.
+ */
+
+int
+iobrick_pci_pwr( l1sc_t *sc, int bus, int slot, int req_code )
+{
+#if 0 /* The "bedrock request" method of performing this function
+       * seems to be broken in the L1, so for now use the command-
+       * interpreter method
+       */
+
+    char	msg[BRL1_QSIZE];
+    int		len;    /* length of message being sent */
+    int		subch;  /* system controller subchannel used */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( sc, L1_ADDR_LOCALIO );
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 req_code, 4,
+				 L1_ARG_INT, bus,
+				 L1_ARG_INT, slot )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND(sc, subch, msg, msg, &len ) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 0 ) < 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    return 0;
+
+#else
+    char cmd[64];
+    char *fxn;
+
+    switch( req_code )
+    {
+    case L1_REQ_PCI_UP:
+	fxn = "u";
+	break;
+    case L1_REQ_PCI_DOWN:
+	fxn = "d";
+	break;
+    case L1_REQ_PCI_RESET:
+	fxn = "rst";
+	break;
+    default:
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    if( slot == -1 ) 
+	sprintf( cmd, "pci %d %s", bus, fxn );
+    else
+        sprintf( cmd, "pci %d %d %s", bus, slot, fxn );
+	
+    return sc_command_interp( sc, L1_ADDR_TYPE_IOBRICK,
+	L1_ADDR_RACK_LOCAL, L1_ADDR_BAY_LOCAL, cmd );
+#endif
+}
+				 
+int
+iobrick_pci_slot_pwr( l1sc_t *sc, int bus, int slot, int up )
+{
+    return iobrick_pci_pwr( sc, bus, slot, up );
+}
+
+int
+iobrick_pci_bus_pwr( l1sc_t *sc, int bus, int up )
+{
+    return iobrick_pci_pwr( sc, bus, -1, up );
+}
+
+
+int
+iobrick_pci_slot_rst( l1sc_t *sc, int bus, int slot )
+{
+    return iobrick_pci_pwr( sc, bus, slot, L1_REQ_PCI_RESET );
+}
+
+int
+iobrick_pci_bus_rst( l1sc_t *sc, int bus )
+{
+    return iobrick_pci_pwr( sc, bus, -1, L1_REQ_PCI_RESET );
+}
+
+
+/* get the L1 firmware version for an iobrick */
+int
+iobrick_sc_version( l1sc_t *sc, char *result )
+{
+    char	msg[BRL1_QSIZE];
+    int		len;    /* length of message being sent */
+    int		subch;  /* system controller subchannel used */
+    int		major,  /* major rev number */
+	        minor,  /* minor rev number */
+                bugfix; /* bugfix rev number */
+
+    /* fill in msg with the opcode & params */
+    bzero( msg, BRL1_QSIZE );
+    subch = sc_open( sc, L1_ADDR_LOCALIO );
+
+    if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_FW_REV, 0 )) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_ARGS );
+    }
+
+    /* send the request to the L1 */
+    if( SC_COMMAND(sc, subch, msg, msg, &len ) < 0 )
+    {
+	sc_close( sc, subch );
+	return( ELSC_ERROR_CMD_SEND );
+    }
+
+    /* free up subchannel */
+    sc_close( sc, subch );
+
+    /* check response */
+    if( sc_interpret_resp( msg, 6, L1_ARG_INT, &major,
+			   L1_ARG_INT, &minor, L1_ARG_INT, &bugfix )
+	< 0 )
+    {
+	return( ELSC_ERROR_RESP_FORMAT );
+    }
+
+    sprintf( result, "%d.%d.%d", major, minor, bugfix );
+
+    return 0;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)