summaryrefslogtreecommitdiff
path: root/lib/ipmi_kontronoem.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ipmi_kontronoem.c')
-rw-r--r--lib/ipmi_kontronoem.c808
1 files changed, 808 insertions, 0 deletions
diff --git a/lib/ipmi_kontronoem.c b/lib/ipmi_kontronoem.c
new file mode 100644
index 0000000..c154eda
--- /dev/null
+++ b/lib/ipmi_kontronoem.c
@@ -0,0 +1,808 @@
+/*
+ * Copyright (c) 2004 Kontron Canada, Inc. All Rights Reserved.
+ *
+ * Base on code from
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * Tue Mar 7 14:36:12 2006
+ * <stephane.filion@ca.kontron.com>
+ *
+ * This code implements an Kontron OEM proprietary commands.
+ */
+
+
+#include <string.h>
+#include <ipmitool/helper.h>
+#include <ipmitool/ipmi.h>
+#include <ipmitool/ipmi_intf.h>
+#include <ipmitool/ipmi_fru.h>
+
+
+extern int verbose;
+extern int read_fru_area(struct ipmi_intf * intf, struct fru_info *fru,
+ uint8_t id, uint32_t offset, uint32_t length,
+ uint8_t *frubuf);
+extern int write_fru_area(struct ipmi_intf * intf, struct fru_info *fru,
+ unsigned char id, unsigned int soffset,
+ unsigned int doffset, unsigned int length,
+ unsigned char *pFrubuf);
+
+extern char * get_fru_area_str(uint8_t * data, uint32_t * offset);
+
+
+
+static void ipmi_kontron_help(void);
+static int ipmi_kontron_set_serial_number(struct ipmi_intf * intf);
+static int ipmi_kontron_set_mfg_date (struct ipmi_intf * intf);
+
+static void ipmi_kontron_nextboot_help(void);
+static int ipmi_kontron_nextboot_set(struct ipmi_intf * intf,
+ int argc, char **argv);
+
+static int ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf * intf,
+ unsigned char channel,
+ unsigned char size);
+
+int
+ipmi_kontronoem_main(struct ipmi_intf * intf, int argc, char ** argv)
+{
+ int rc = 0;
+
+ if (argc == 0)
+ ipmi_kontron_help();
+ else if (strncmp(argv[0], "help", 4) == 0)
+ ipmi_kontron_help();
+
+ else if (!strncmp(argv[0], "setsn", 5))
+ {
+ if(argc >= 1)
+ {
+ if(ipmi_kontron_set_serial_number(intf) > 0)
+ {
+ printf("FRU serial number setted successfully\n");
+ }
+ else
+ {
+ printf("FRU serial number set failed\n");
+ }
+ }
+ else
+ {
+ printf("fru setsn\n");
+ }
+ }
+ else if (!strncmp(argv[0], "setmfgdate", 5))
+ {
+ if(argc >= 1)
+ {
+ if(ipmi_kontron_set_mfg_date(intf) > 0)
+ {
+ printf("FRU manufacturing date setted successfully\n");
+ }
+ else
+ {
+ printf("FRU manufacturing date set failed\n");
+ }
+ }
+ else
+ {
+ printf("fru setmfgdate\n");
+ }
+ }
+ else if (!strncmp(argv[0], "nextboot", sizeof("nextboot")-1))
+ {
+ if (argc > 1)
+ {
+ if ((rc = ipmi_kontron_nextboot_set(intf, argc-1, argv+1)) == 0)
+ {
+ printf("Nextboot set successfully\n");
+ }
+ else
+ {
+ printf("Nextboot set failed\n");
+ }
+ }
+ else
+ {
+ ipmi_kontron_nextboot_help();
+ }
+ }
+
+ else
+ {
+ printf("Invalid Kontron command: %s", argv[0]);
+ ipmi_kontron_help();
+ rc = -1;
+ }
+
+ return rc;
+}
+
+
+static void ipmi_kontron_help(void)
+{
+ printf("Kontron Commands: setsn setmfgdate nextboot\n");
+}
+
+
+int ipmi_kontronoem_set_large_buffer(struct ipmi_intf * intf, unsigned char size)
+{
+ uint8_t error_occurs = 0;
+ uint32_t prev_target_addr = intf->target_addr ;
+
+ if ( intf->target_addr > 0 && (intf->target_addr != intf->my_addr) )
+ {
+ intf->target_addr = intf->my_addr;
+
+ printf("Set local big buffer\n");
+ if(ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, size ) == 0)
+ {
+ printf("Set local big buffer:success\n");
+ }
+ else
+ {
+ error_occurs = 1;
+ }
+
+ if (error_occurs == 0)
+ {
+ if(ipmi_kontronoem_send_set_large_buffer( intf, 0x00, size ) == 0)
+ {
+ printf("IPMB was set\n");
+ }
+ else
+ {
+ /* Revert back the previous set large buffer */
+ error_occurs = 1;
+ ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, 0 );
+ }
+ }
+
+ /* Restore target address */
+ intf->target_addr = prev_target_addr;
+ }
+
+ if (error_occurs == 0)
+ {
+ if(ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, size ) == 0)
+ {
+ //printf("Set remote big buffer\n");
+ }
+ else
+ {
+ if ( intf->target_addr > 0 && (intf->target_addr != intf->my_addr) )
+ {
+ /* Error occurs revert back the previous set large buffer*/
+ intf->target_addr = intf->my_addr;
+
+ //ipmi_kontronoem_send_set_large_buffer( intf, 0x00, 0 );
+ ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, 0 );
+
+ intf->target_addr = prev_target_addr;
+ }
+ }
+ }
+ return error_occurs;
+}
+
+
+int ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf * intf, unsigned char channel,unsigned char size)
+{
+ struct ipmi_rs *rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[2];
+ int i;
+
+ memset(msg_data, 0, sizeof(msg_data));
+ msg_data[0] = channel/*0x0e*/; // Currently running interface
+ msg_data[1] = size;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = 0x3E; /* OEM NetFn */
+ req.msg.cmd = 0x82; /* Set Channel Buffer Length - OEM */
+ req.msg.data = msg_data;
+ req.msg.data_len = 2;
+
+ req.msg.lun = 0x00;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf("Cannot send large buffer command\n");
+ return(-1);
+ }
+ if (rsp->ccode > 0)
+ {
+ printf("Invalid length for the selected interface (%s) %d\n",
+ val2str(rsp->ccode, completion_code_vals), rsp->ccode);
+ return(-1);
+ }
+ return 0;
+}
+
+
+/* ipmi_fru_set_serial_number - Set the Serial Number in FRU
+ *
+ * @intf: ipmi interface
+ * @id: fru id
+ *
+ * returns -1 on error
+ * returns 1 if successful
+ */
+static int
+ipmi_kontron_set_serial_number(struct ipmi_intf * intf)
+{
+ struct ipmi_rs *rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ struct fru_header header;
+ uint8_t msg_data[4];
+ char *sn;
+ uint8_t sn_size, checksum;
+ uint8_t *fru_data;
+ char *fru_area;
+ uint32_t fru_data_offset, fru_data_offset_tmp, board_sec_len, prod_sec_len, i;
+
+ sn = NULL;
+ fru_data = NULL;
+
+ memset(msg_data, 0, 4);
+ msg_data[0] = 0xb4;
+ msg_data[1] = 0x90;
+ msg_data[2] = 0x91;
+ msg_data[3] = 0x8b;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = 0x3E;
+ req.msg.cmd = 0x0C;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+
+ /* Set Lun, necessary for this oem command */
+ req.msg.lun = 0x03;
+
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf(" Device not present (No Response)\n");
+ return -11;
+ }
+
+ if (rsp->ccode > 0)
+ {
+ printf(" This option is not implemented for this board\n");
+ return -1;
+ }
+
+ sn_size = rsp->data_len;
+
+ sn = malloc(sn_size + 1);
+
+ if(sn == NULL)
+ {
+ printf("Out of memory!");
+ return -1;
+ }
+
+ memset(sn, 0, sn_size + 1);
+ memcpy(sn, rsp->data, sn_size);
+
+ if(verbose >= 1)
+ {
+ printf("Original serial number is : [%s]\n", sn);
+ }
+
+
+
+ memset(msg_data, 0, 4);
+ msg_data[0] = 0;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ free(sn);
+ sn = NULL;
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ free(sn);
+ sn = NULL;
+ return(-1);
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ if (fru.size < 1) {
+ printf(" Invalid FRU size %d", fru.size);
+ free(sn);
+ sn = NULL;
+ return -1;
+ }
+
+ /*
+ * retrieve the FRU header
+ */
+ msg_data[0] = 0;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf(" Device not present (No Response)\n");
+ free(sn);
+ sn = NULL;
+ return(-1);
+ }
+ if (rsp->ccode > 0)
+ {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ free(sn);
+ sn = NULL;
+ return(-1);
+ }
+
+ if (verbose > 1)
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ if (header.version != 1)
+ {
+ printf(" Unknown FRU header version 0x%02x",
+ header.version);
+ free(sn);
+ sn = NULL;
+ return(-1);
+ }
+
+ /* Set the Board Section */
+ board_sec_len = (header.offset.product * 8) - (header.offset.board * 8);
+
+
+ fru_data = malloc( fru.size);
+
+ if(fru_data == NULL)
+ {
+ printf("Out of memory!");
+ free(sn);
+ sn = NULL;
+ return(-1);
+ }
+
+ memset(fru_data, 0, fru.size);
+ if(read_fru_area(intf ,&fru ,0 ,(header.offset.board * 8) ,board_sec_len , fru_data) < 0)
+ {
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+ return(-1);
+ }
+
+ /*Position at Board Manufacturer*/
+ fru_data_offset = (header.offset.board * 8) + 6;
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset);
+
+ /*Position at Board Product Name*/
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset);
+
+ fru_data_offset_tmp = fru_data_offset;
+
+ /*Position at Serial Number*/
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp);
+
+ fru_data_offset ++;
+
+ if(strlen(fru_area) != sn_size)
+ {
+ printf("The length of the serial number in the FRU Board Area is wrong.\n");
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+ return(-1);
+ }
+
+ /* Copy the new serial number in the board section saved in memory*/
+ memcpy(fru_data + fru_data_offset, sn, sn_size);
+
+ checksum = 0;
+ /* Calculate Header Checksum */
+ for( i = (header.offset.board * 8); i < (((header.offset.board * 8)+board_sec_len) - 2) ; i ++ )
+ {
+ checksum += fru_data[i];
+ }
+ checksum = (~checksum) + 1;
+
+
+ fru_data[(header.offset.board * 8)+board_sec_len - 1] = checksum;
+
+ /* Write the new FRU Board section */
+ if(write_fru_area(intf, &fru, 0, (header.offset.board * 8), (header.offset.board * 8), board_sec_len, fru_data) < 0)
+ {
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+ return(-1);
+ }
+
+ /* Set the Product Section */
+ prod_sec_len = (header.offset.multi * 8) - (header.offset.product * 8);
+
+ if(read_fru_area(intf ,&fru ,0 ,(header.offset.product * 8) ,prod_sec_len , fru_data) < 0)
+ {
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+ return(-1);
+ }
+
+ /*Position at Product Manufacturer*/
+ fru_data_offset = (header.offset.product * 8) + 3;
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset);
+
+ /*Position at Product Name*/
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset);
+
+ /*Position at Product Part*/
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset);
+
+ /*Position at Product Version*/
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset);
+
+
+
+ fru_data_offset_tmp = fru_data_offset;
+
+ /*Position at Serial Number*/
+ fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp);
+
+ fru_data_offset ++;
+
+ if(strlen(fru_area) != sn_size)
+ {
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+ printf("The length of the serial number in the FRU Product Area is wrong.\n");
+ return(-1);
+
+ }
+
+ /* Copy the new serial number in the product section saved in memory*/
+ memcpy(fru_data + fru_data_offset, sn, sn_size);
+
+ checksum = 0;
+ /* Calculate Header Checksum */
+ for( i = (header.offset.product * 8); i < (((header.offset.product * 8)+prod_sec_len) - 2) ; i ++ )
+ {
+ checksum += fru_data[i];
+ }
+ checksum = (~checksum) + 1;
+
+
+ fru_data[(header.offset.product * 8)+prod_sec_len - 1] = checksum;
+
+ /* Write the new FRU Board section */
+ if(write_fru_area(intf, &fru, 0, (header.offset.product * 8), (header.offset.product * 8), prod_sec_len, fru_data) < 0)
+ {
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+ return -1;
+ }
+
+ free(sn);
+ sn = NULL;
+ free(fru_data);
+ fru_data = NULL;
+
+ return(1);
+
+}
+
+
+/* ipmi_fru_set_mfg_date - Set the Manufacturing Date in FRU
+ *
+ * @intf: ipmi interface
+ * @id: fru id
+ *
+ * returns -1 on error
+ * returns 1 if successful
+ */
+static int
+ipmi_kontron_set_mfg_date (struct ipmi_intf * intf)
+{
+ struct ipmi_rs *rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ struct fru_header header;
+ uint8_t msg_data[4];
+ uint8_t mfg_date[3];
+
+ uint32_t board_sec_len, i;
+ uint8_t *fru_data, checksum;
+
+
+
+
+ memset(msg_data, 0, 4);
+ msg_data[0] = 0xb4;
+ msg_data[1] = 0x90;
+ msg_data[2] = 0x91;
+ msg_data[3] = 0x8b;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = 0x3E;
+ req.msg.cmd = 0x0E;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ /* Set Lun temporary, necessary for this oem command */
+ req.msg.lun = 0x03;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf("Device not present (No Response)\n");
+ return(-1);
+ }
+
+ if (rsp->ccode > 0)
+ {
+ printf("This option is not implemented for this board\n");
+ return(-1);
+ }
+
+ if(rsp->data_len != 3)
+ {
+ printf("Invalid response for the Manufacturing date\n");
+ return(-1);
+ }
+
+ memset(mfg_date, 0, 3);
+ memcpy(mfg_date, rsp->data, 3);
+
+ memset(msg_data, 0, 4);
+ msg_data[0] = 0;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return(-1);
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return(-1);
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ if (fru.size < 1) {
+ printf(" Invalid FRU size %d", fru.size);
+ return(-1);
+ }
+
+ /*
+ * retrieve the FRU header
+ */
+ msg_data[0] = 0;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf(" Device not present (No Response)\n");
+ return(-1);
+ }
+ if (rsp->ccode > 0)
+ {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return(-1);
+ }
+
+ if (verbose > 1)
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ if (header.version != 1)
+ {
+ printf(" Unknown FRU header version 0x%02x",
+ header.version);
+ return(-1);
+ }
+
+
+ board_sec_len = (header.offset.product * 8) - (header.offset.board * 8);
+
+ fru_data = malloc( fru.size);
+
+ if(fru_data == NULL)
+ {
+ printf("Out of memory!");
+ return(-1);
+ }
+
+ memset(fru_data, 0, fru.size);
+ if(read_fru_area(intf ,&fru ,0 ,(header.offset.board * 8) ,board_sec_len , fru_data) < 0)
+ {
+ free(fru_data);
+ fru_data = NULL;
+ return(-1);
+ }
+
+ /* Copy the new manufacturing date in the board section saved in memory*/
+ memcpy(fru_data + (header.offset.board * 8) + 3, mfg_date, 3);
+
+ checksum = 0;
+ /* Calculate Header Checksum */
+ for( i = (header.offset.board * 8); i < (((header.offset.board * 8)+board_sec_len) - 2) ; i ++ )
+ {
+ checksum += fru_data[i];
+ }
+ checksum = (~checksum) + 1;
+
+ fru_data[(header.offset.board * 8)+board_sec_len - 1] = checksum;
+
+ /* Write the new FRU Board section */
+ if(write_fru_area(intf, &fru, 0, (header.offset.board * 8), (header.offset.board * 8), board_sec_len, fru_data) < 0)
+ {
+ free(fru_data);
+ fru_data = NULL;
+ return(-1);
+ }
+
+ free(fru_data);
+ fru_data = NULL;
+ return(1);
+}
+
+
+static char *bootdev[] = {"BIOS", "FDD", "HDD", "CDROM", "network", 0};
+
+static void
+ipmi_kontron_nextboot_help(void)
+{
+ int i;
+ printf("nextboot <device>\n"
+ "Supported devices:\n");
+ for (i = 0; bootdev[i] != 0; i++) {
+ printf("- %s\n", bootdev[i]);
+ }
+}
+
+/* ipmi_kontron_next_boot_set - Select the next boot order on CP6012
+ *
+ * @intf: ipmi interface
+ * @id: fru id
+ *
+ * returns -1 on error
+ * returns 1 if successful
+ */
+static int
+ipmi_kontron_nextboot_set(struct ipmi_intf * intf, int argc, char **argv)
+{
+ struct ipmi_rs *rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[8];
+ int i;
+
+ memset(msg_data, 0, sizeof(msg_data));
+ msg_data[0] = 0xb4;
+ msg_data[1] = 0x90;
+ msg_data[2] = 0x91;
+ msg_data[3] = 0x8b;
+ msg_data[4] = 0x9d;
+ msg_data[5] = 0xFF;
+ msg_data[6] = 0xFF; /* any */
+
+ for (i = 0; bootdev[i] != 0; i++) {
+ if (strcmp(argv[0], bootdev[i]) == 0) {
+ msg_data[5] = i;
+ break;
+ }
+ }
+
+ /* Invalid device selected? */
+ if (msg_data[5] == 0xFF) {
+ printf("Unknown boot device: %s\n", argv[0]);
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = 0x3E;
+ req.msg.cmd = 0x02;
+ req.msg.data = msg_data;
+ req.msg.data_len = 7;
+
+ /* Set Lun temporary, necessary for this oem command */
+ req.msg.lun = 0x03;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf("Device not present (No Response)\n");
+ return(-1);
+ }
+ if (rsp->ccode > 0) {
+ printf("Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return(-1);
+ }
+ return 0;
+}
+