root/usr/src/cmd/scadm/sparc/mpxu/common/download.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * download.c: support to the scadm download option (download service
 * processor firmware)
 */

#include <libintl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>  /* required by rsc.h */

#include "adm.h"
#include "librsc.h"
#include "smq.h"


extern smq_t            ADM_bpMsgQueue;
extern smq_msg_t        ADM_bpMsgBuffer[ADM_BP_BUFF_SIZE];

static void usage();


void
ADM_Process_download(int argc, char *argv[])
{
        int                     BootRetry;
        uint8_t                 DownloadLocation;
        static bp_msg_t         Message;
        static struct timespec  Timeout;
        char                    *Filename;
        FILE                    *FilePtr;
        timestruc_t             Delay;
        int                     i, err;
        int retry;
        int bootOpt;

        if ((argc != 3) && (argc != 4)) {
                usage();
                exit(-1);
        }

        if (argc == 4) {
                if (strcasecmp(argv[2], "boot") != 0) {
                        usage();
                        exit(-1);
                }
                Filename = argv[3];
                DownloadLocation = BP_DAT2_FLASH_BOOT;
                bootOpt = 1;
        } else { /* no [boot] option */

                Filename = argv[2];
                DownloadLocation = BP_DAT2_FLASH_MAIN;
                bootOpt = 0;
        }

        if ((FilePtr = fopen(Filename, "r")) == NULL) {
                (void) fprintf(stderr, "\n%s - \"%s\"\n\n",
                    gettext("scadm: file could not be opened"), Filename);
                exit(-1);
        }


        /* Verify file is s-record */
        if (ADM_Valid_srecord(FilePtr) != 0) {
                (void) fprintf(stderr, "\n%s - \"%s\"\n\n",
                    gettext("scadm: file not a valid s-record"), Filename);
                exit(-1);
        }

        /*
         * Don't call rscp_start() because SC may still be in the
         * boot monitor.  The boot monitor will not respond to
         * rscp_start()
         */

        /*
         * Initialize Message Queue used between ADM_Callback and
         * ADM_Boot_recv(). ADM_Callback is called from seperate thread.
         */
        if (smq_init(&ADM_bpMsgQueue, ADM_bpMsgBuffer,
            ADM_BP_BUFF_SIZE) != 0) {

                (void) fprintf(stderr, "\n%s\n\n",
                    gettext("scadm: ERROR, unable to setup message queue"));
                exit(-1);
        }

        /* Initialize callback for Boot Monitor RX */
        if (rscp_register_bpmsg_cb(ADM_Callback) != 0) {
                (void) fprintf(stderr, "\n%s\n\n",
                    gettext("scadm: ERROR, callback init failed"));
                exit(-1);
        }

        BootRetry = ADM_BOOT_RETRY;
        while (BootRetry > 0) {

                /*
                 * Initialize Message each time because this structure is reused
                 * during receive.  Since this operation is not time critical,
                 * this is not a concern
                 */
                Message.cmd  = BP_OBP_BOOTINIT;
                Message.dat1 = 0;
                Message.dat2 = DownloadLocation;
                rscp_send_bpmsg(&Message);

                /*
                 * Initialize Timeout each time just to be robust. Since this
                 * operation is not time critical, this is not a concern.
                 */
                Timeout.tv_nsec = 0;
                Timeout.tv_sec = ADM_BOOT_INIT_TIMEOUT;

                /* If we timeout, decrement BootRetry and try again */
                if (ADM_Boot_recv(&Message, &Timeout) != 0) {

                        /* We got a timeout */
                        BootRetry = BootRetry - 1;
                        continue;
                } else {

                        /* we got a message back, see what it is */
                        if ((Message.cmd  != BP_RSC_BOOTACK) ||
                            (Message.dat1 != BP_DAT1_BOOTINIT_ACK)) {

                                ADM_Display_download_error(Message.cmd,
                                    Message.dat1);
                                exit(-1);
                        }

                        /*
                         * We got a valid acknowledge, break out of loop and
                         * start to download s-record
                         */
                        break;
                }
        }

        /* See if we ever got a response */
        if (BootRetry <= 0) {
                (void) fprintf(stderr, "\n%s\n\n",
                    gettext("scadm: SC did not respond during boot "
                    "initialization"));
                exit(-1);
        }

        /* Download s-record */
        if (ADM_Send_file(FilePtr) != 0) {
                (void) fprintf(stderr, "\n%s - \"%s\"\n\n",
                    gettext("scadm: Error downloading file"), Filename);
                exit(-1);
        }

        /* wait a second for BootMonitor to catch up */
        Delay.tv_nsec = 0;
        Delay.tv_sec  = 1;
        (void) nanosleep(&Delay, NULL);

        /* Send Reset boot protocol message to reboot SC */
        Message.cmd  = BP_OBP_RESET;
        Message.dat1 = 0;
        Message.dat2 = 0;
        rscp_send_bpmsg(&Message);

        /* Cleanup */
        rscp_unregister_bpmsg_cb(ADM_Callback);
        (void) smq_destroy(&ADM_bpMsgQueue);
        (void) fclose(FilePtr);

        (void) printf("%s\n\n", gettext("Download completed successfully"));

        (void) printf("%s\n\n", gettext("Please wait for verification"));

        /*
         * scadm cannot tell if the SC successfully verified the
         * download or not, but instead attempts to send a
         * status message (up to 60 times) and assumes proper
         * operation when sucessfully sent.
         *
         * When the boot option is used, the SC may hang after
         * resetting itself (after it sucessfully downloads and
         * verifies the boot file).  To work around this, scadm
         * will (1) do a hard reset and pause for 10 seconds
         * (2) retry the sending of status messages.
         */

        retry = 0;
        do {
                if (retry == 1) {
                        /* reset the SC before retrying */
                        if (rsc_nmi() != 0) {
                                (void) fprintf(stderr, "\n%s\n\n",
                                    gettext(
                                    "scadm: Unable to reset SC hardware"));
                                exit(-1);
                        }
                        /* delay while SC resets */
                        Delay.tv_nsec = 0;
                        Delay.tv_sec  = ADM_BOOT_LOAD_TIMEOUT;
                        (void) nanosleep(&Delay, NULL);
                }

                for (i = 0; i < 60; i++) {
                        rscp_msg_t msg;
                        msg.type = DP_RSC_STATUS;
                        msg.len = 0;
                        msg.data = NULL;

                        (void) printf("%s", gettext("."));
                        (void) fflush(stdout);

                        err = rscp_send(&msg);
                        if (err == 0)
                                break;
                }
                if (err == 0)
                        break;
                retry++;
        } while (bootOpt && (retry < 2));
        if (err == 0)
                (void) printf("\n%s\n\n", gettext("Complete"));
        else
                (void) printf("\n%s\n\n", gettext("Error during verification"));
}


static void
usage()
{
        (void) fprintf(stderr, "\n%s\n\n",
            gettext("USAGE: scadm download [boot] <file>"));
}