xref: /openbmc/ipmitool/lib/ipmi_kontronoem.c (revision 6ca88cb6)
1 /*
2  * Copyright (c) 2004 Kontron Canada, Inc.  All Rights Reserved.
3  *
4  * Base on code from
5  * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * Redistribution of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * Redistribution in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any kind.
23  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
24  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
25  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
26  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
27  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
28  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
29  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
30  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
31  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
32  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
33  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34  */
35 
36 /*
37  * Tue Mar 7 14:36:12 2006
38  * <stephane.filion@ca.kontron.com>
39  *
40  * This code implements an Kontron OEM proprietary commands.
41  */
42 #include <string.h>
43 #include <ipmitool/helper.h>
44 #include <ipmitool/log.h>
45 #include <ipmitool/ipmi.h>
46 #include <ipmitool/ipmi_intf.h>
47 #include <ipmitool/ipmi_fru.h>
48 
49 extern int verbose;
50 extern int read_fru_area(struct ipmi_intf *intf, struct fru_info *fru,
51 		uint8_t id, uint32_t offset, uint32_t length,
52 		uint8_t *frubuf);
53 extern int write_fru_area(struct ipmi_intf * intf, struct fru_info *fru,
54 		uint8_t id, uint16_t soffset,
55 		uint16_t doffset,  uint16_t length,
56 		uint8_t *pFrubuf);
57 extern char *get_fru_area_str(uint8_t *data, uint32_t *offset);
58 
59 static void ipmi_kontron_help(void);
60 static int ipmi_kontron_set_serial_number(struct ipmi_intf *intf);
61 static int ipmi_kontron_set_mfg_date (struct ipmi_intf *intf);
62 static void ipmi_kontron_nextboot_help(void);
63 static int ipmi_kontron_nextboot_set(struct ipmi_intf *intf, int argc,
64 		char **argv);
65 static int ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf *intf,
66 		unsigned char channel, unsigned char size);
67 
68 static char *bootdev[] = {"BIOS", "FDD", "HDD", "CDROM", "network", 0};
69 
70 int
71 ipmi_kontronoem_main(struct ipmi_intf *intf, int argc, char **argv)
72 {
73 	int rc = 0;
74 	if (argc == 0) {
75 		lprintf(LOG_ERR, "Not enough parameters given.");
76 		ipmi_kontron_help();
77 		return (-1);
78 	}
79 	if (strncmp(argv[0], "help", 4) == 0) {
80 		ipmi_kontron_help();
81 		rc = 0;
82 	} else if (!strncmp(argv[0], "setsn", 5)) {
83 		if (argc < 1) {
84 			printf("fru setsn\n");
85 			return (-1);
86 		}
87 		if (ipmi_kontron_set_serial_number(intf) > 0) {
88 			printf("FRU serial number setted successfully\n");
89 		} else {
90 			printf("FRU serial number set failed\n");
91 			rc = (-1);
92 		}
93 	} else if (!strncmp(argv[0], "setmfgdate", 10)) {
94 		if (argc < 1) {
95 			printf("fru setmfgdate\n");
96 			return (-1);
97 		}
98 		if (ipmi_kontron_set_mfg_date(intf) > 0) {
99 			printf("FRU manufacturing date setted successfully\n");
100 		} else {
101 			printf("FRU manufacturing date set failed\n");
102 			rc = (-1);
103 		}
104 	} else if (!strncmp(argv[0], "nextboot", 8)) {
105 		if (argc < 2) {
106 			lprintf(LOG_ERR, "Not enough parameters given.");
107 			ipmi_kontron_nextboot_help();
108 			rc = (-1);
109 		}
110 		rc = ipmi_kontron_nextboot_set(intf, (argc - 1), (argv + 1));
111 		if (rc == 0) {
112 			printf("Nextboot set successfully\n");
113 		} else {
114 			printf("Nextboot set failed\n");
115 			rc = (-1);
116 		}
117 	} else  {
118 		lprintf(LOG_ERR, "Invalid Kontron command: %s", argv[0]);
119 		ipmi_kontron_help();
120 		rc = (-1);
121 	}
122 	return rc;
123 }
124 
125 static void
126 ipmi_kontron_help(void)
127 {
128 	printf("Kontron Commands:  setsn setmfgdate nextboot\n");
129 }
130 
131 int
132 ipmi_kontronoem_set_large_buffer(struct ipmi_intf *intf, unsigned char size)
133 {
134 	uint8_t error_occurs = 0;
135 	uint32_t prev_target_addr = intf->target_addr ;
136 	if (intf->target_addr > 0 && (intf->target_addr != intf->my_addr)) {
137 		intf->target_addr = intf->my_addr;
138 		printf("Set local big buffer\n");
139 		if (ipmi_kontronoem_send_set_large_buffer(intf, 0x0e, size) == 0) {
140 			printf("Set local big buffer:success\n");
141 		} else {
142 			error_occurs = 1;
143 		}
144 		if (error_occurs == 0) {
145 			if (ipmi_kontronoem_send_set_large_buffer(intf, 0x00, size) == 0) {
146 				printf("IPMB was set\n");
147 			} else {
148 				/* Revert back the previous set large buffer */
149 				error_occurs = 1;
150 				ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, 0 );
151 			}
152 		}
153 		/* Restore target address */
154 		intf->target_addr = prev_target_addr;
155 	}
156 	if (error_occurs == 0) {
157 		if(ipmi_kontronoem_send_set_large_buffer(intf, 0x0e, size) == 0) {
158 			/* printf("Set remote big buffer\n"); */
159 		} else {
160 			if (intf->target_addr > 0  && (intf->target_addr != intf->my_addr)) {
161 				/* Error occurs revert back the previous set large buffer */
162 				intf->target_addr = intf->my_addr;
163 				/* ipmi_kontronoem_send_set_large_buffer(intf, 0x00, 0); */
164 				ipmi_kontronoem_send_set_large_buffer(intf, 0x0e, 0);
165 				intf->target_addr = prev_target_addr;
166 			}
167 		}
168 	}
169 	return error_occurs;
170 }
171 
172 int
173 ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf *intf,
174 		unsigned char channel, unsigned char size)
175 {
176 	struct ipmi_rs *rsp;
177 	struct ipmi_rq req;
178 	uint8_t msg_data[2];
179 	int i;
180 	memset(msg_data, 0, sizeof(msg_data));
181 	/* channel =~ 0x0e => Currently running interface */
182 	msg_data[0] = channel;
183 	msg_data[1] = size;
184 	memset(&req, 0, sizeof(req));
185 	req.msg.netfn = 0x3E;
186 	/* Set Channel Buffer Length - OEM */
187 	req.msg.cmd = 0x82;
188 	req.msg.data = msg_data;
189 	req.msg.data_len = 2;
190 	req.msg.lun = 0x00;
191 	rsp = intf->sendrecv(intf, &req);
192 	if (rsp == NULL)  {
193 		printf("Cannot send large buffer command\n");
194 		return(-1);
195 	} else if (rsp->ccode > 0)  {
196 		printf("Invalid length for the selected interface (%s) %d\n",
197 				val2str(rsp->ccode, completion_code_vals), rsp->ccode);
198 		return(-1);
199 	}
200 	return 0;
201 }
202 
203 /* ipmi_fru_set_serial_number -  Set the Serial Number in FRU
204  *
205  * @intf: ipmi interface
206  * @id: fru id
207  *
208  * returns -1 on error
209  * returns 1 if successful
210  */
211 static int
212 ipmi_kontron_set_serial_number(struct ipmi_intf *intf)
213 {
214 	struct fru_header header;
215 	struct fru_info fru;
216 	struct ipmi_rs *rsp;
217 	struct ipmi_rq req;
218 	char *sn;
219 	char *fru_area;
220 	uint8_t checksum;
221 	uint8_t *fru_data;
222 	uint8_t msg_data[4];
223 	uint8_t sn_size;
224 	uint32_t board_sec_len;
225 	uint32_t fru_data_offset;
226 	uint32_t fru_data_offset_tmp;
227 	uint32_t i;
228 	uint32_t prod_sec_len;
229 
230 	sn = NULL;
231 	fru_data = NULL;
232 
233 	memset(msg_data, 0, 4);
234 	msg_data[0] = 0xb4;
235 	msg_data[1] = 0x90;
236 	msg_data[2] = 0x91;
237 	msg_data[3] = 0x8b;
238 
239 	memset(&req, 0, sizeof(req));
240 	req.msg.netfn = 0x3E;
241 	req.msg.cmd = 0x0C;
242 	req.msg.data = msg_data;
243 	req.msg.data_len = 4;
244 	/* Set Lun, necessary for this oem command */
245 	req.msg.lun = 0x03;
246 	rsp = intf->sendrecv(intf, &req);
247 	if (rsp == NULL) {
248 		printf(" Device not present (No Response)\n");
249 		return (-1);
250 	} else if (rsp->ccode > 0) {
251 		printf(" This option is not implemented for this board\n");
252 		return (-1);
253 	}
254 	sn_size = rsp->data_len;
255 	sn = malloc(sn_size + 1);
256 	if (sn == NULL) {
257 		lprintf(LOG_ERR, "ipmitool: malloc failure");
258 		return (-1);
259 	}
260 	memset(sn, 0, sn_size + 1);
261 	memcpy(sn, rsp->data, sn_size);
262 	if (verbose >= 1) {
263 		printf("Original serial number is : [%s]\n", sn);
264 	}
265 	memset(msg_data, 0, 4);
266 	msg_data[0] = 0;
267 	memset(&req, 0, sizeof(req));
268 	req.msg.netfn = IPMI_NETFN_STORAGE;
269 	req.msg.cmd = GET_FRU_INFO;
270 	req.msg.data = msg_data;
271 	req.msg.data_len = 1;
272 	rsp = intf->sendrecv(intf, &req);
273 	if (rsp == NULL) {
274 		printf(" Device not present (No Response)\n");
275 		free(sn);
276 		sn = NULL;
277 		return (-1);
278 	} else if (rsp->ccode > 0) {
279 		printf(" Device not present (%s)\n",
280 				val2str(rsp->ccode, completion_code_vals));
281 		free(sn);
282 		sn = NULL;
283 		return (-1);
284 	}
285 	memset(&fru, 0, sizeof(fru));
286 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
287 	fru.access = rsp->data[2] & 0x1;
288 	if (fru.size < 1) {
289 		printf(" Invalid FRU size %d", fru.size);
290 		free(sn);
291 		sn = NULL;
292 		return (-1);
293 	}
294 	/* retrieve the FRU header */
295 	msg_data[0] = 0;
296 	msg_data[1] = 0;
297 	msg_data[2] = 0;
298 	msg_data[3] = 8;
299 
300 	memset(&req, 0, sizeof(req));
301 	req.msg.netfn = IPMI_NETFN_STORAGE;
302 	req.msg.cmd = GET_FRU_DATA;
303 	req.msg.data = msg_data;
304 	req.msg.data_len = 4;
305 	rsp = intf->sendrecv(intf, &req);
306 	if (rsp == NULL) {
307 		printf(" Device not present (No Response)\n");
308 		free(sn);
309 		sn = NULL;
310 		return (-1);
311 	} else if (rsp->ccode > 0) {
312 		printf(" Device not present (%s)\n",
313 				val2str(rsp->ccode, completion_code_vals));
314 		free(sn);
315 		sn = NULL;
316 		return (-1);
317 	}
318 	if (verbose > 1) {
319 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
320 	}
321 	memcpy(&header, rsp->data + 1, 8);
322 	if (header.version != 1) {
323 		printf(" Unknown FRU header version 0x%02x",
324 				header.version);
325 		free(sn);
326 		sn = NULL;
327 		return(-1);
328 	}
329 	/* Set the Board Section */
330 	board_sec_len = (header.offset.product * 8) - (header.offset.board * 8);
331 	fru_data = malloc(fru.size);
332 	if (fru_data == NULL) {
333 		lprintf(LOG_ERR, "ipmitool: malloc failure");
334 		free(sn);
335 		sn = NULL;
336 		return (-1);
337 	}
338 	memset(fru_data, 0, fru.size);
339 	if (read_fru_area(intf, &fru, 0, (header.offset.board * 8),
340 				board_sec_len, fru_data) < 0) {
341 		free(sn);
342 		sn = NULL;
343 		free(fru_data);
344 		fru_data = NULL;
345 		return (-1);
346 	}
347 	/* Position at Board Manufacturer */
348 	fru_data_offset = (header.offset.board * 8) + 6;
349 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
350 	/* Position at Board Product Name */
351 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
352 	fru_data_offset_tmp = fru_data_offset;
353 	/* Position at Serial Number */
354 	fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp);
355 	fru_data_offset++;
356 	if (strlen(fru_area) != sn_size) {
357 		printf("The length of the serial number in the FRU Board Area is wrong.\n");
358 		free(sn);
359 		sn = NULL;
360 		free(fru_data);
361 		fru_data = NULL;
362 		return(-1);
363 	}
364 	/* Copy the new serial number in the board section saved in memory*/
365 	memcpy(fru_data + fru_data_offset, sn, sn_size);
366 	checksum = 0;
367 	/* Calculate Header Checksum */
368 	for(i = (header.offset.board * 8);
369 			i < (((header.offset.board * 8) + board_sec_len) - 2);
370 			i++) {
371 		checksum += fru_data[i];
372 	}
373 	checksum = (~checksum) + 1;
374 	fru_data[(header.offset.board * 8) + board_sec_len - 1] = checksum;
375 	/* Write the new FRU Board section */
376 	if (write_fru_area(intf, &fru, 0, (header.offset.board * 8),
377 				(header.offset.board * 8),
378 				board_sec_len, fru_data) < 0) {
379 		free(sn);
380 		sn = NULL;
381 		free(fru_data);
382 		fru_data = NULL;
383 		return(-1);
384 	}
385 	/* Set the Product Section */
386 	prod_sec_len = (header.offset.multi * 8) - (header.offset.product * 8);
387 	if (read_fru_area(intf, &fru, 0, (header.offset.product * 8),
388 				prod_sec_len, fru_data) < 0) {
389 		free(sn);
390 		sn = NULL;
391 		free(fru_data);
392 		fru_data = NULL;
393 		return(-1);
394 	}
395 	/* Position at Product Manufacturer */
396 	fru_data_offset = (header.offset.product * 8) + 3;
397 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
398 	/* Position at Product Name */
399 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
400 	/* Position at Product Part */
401 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
402 	/* Position at Product Version */
403 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
404 	fru_data_offset_tmp = fru_data_offset;
405 	/* Position at Serial Number */
406 	fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp);
407 	fru_data_offset ++;
408 	if (strlen(fru_area) != sn_size) {
409 		free(sn);
410 		sn = NULL;
411 		free(fru_data);
412 		fru_data = NULL;
413 		printf("The length of the serial number in the FRU Product Area is wrong.\n");
414 		return(-1);
415 	}
416 	/* Copy the new serial number in the product section saved in memory*/
417 	memcpy(fru_data + fru_data_offset, sn, sn_size);
418 	checksum = 0;
419 	/* Calculate Header Checksum */
420 	for (i = (header.offset.product * 8);
421 			i < (((header.offset.product * 8) + prod_sec_len) - 2);
422 			i ++) {
423 		checksum += fru_data[i];
424 	}
425 	checksum = (~checksum) + 1;
426 	fru_data[(header.offset.product * 8)+prod_sec_len - 1] = checksum;
427 	/* Write the new FRU Board section */
428 	if (write_fru_area(intf, &fru, 0, (header.offset.product * 8),
429 				(header.offset.product * 8),
430 				prod_sec_len, fru_data) < 0) {
431 		free(sn);
432 		sn = NULL;
433 		free(fru_data);
434 		fru_data = NULL;
435 		return -1;
436 	}
437 	free(sn);
438 	sn = NULL;
439 	free(fru_data);
440 	fru_data = NULL;
441 	return(1);
442 }
443 
444 /* ipmi_fru_set_mfg_date -  Set the Manufacturing Date in FRU
445  *
446  * @intf: ipmi interface
447  * @id: fru id
448  *
449  * returns -1 on error
450  * returns 1 if successful
451  */
452 static int
453 ipmi_kontron_set_mfg_date (struct ipmi_intf *intf)
454 {
455 	struct fru_header header;
456 	struct fru_info fru;
457 	struct ipmi_rs *rsp;
458 	struct ipmi_rq req;
459 	uint8_t *fru_data;
460 	uint8_t checksum;
461 	uint8_t msg_data[4];
462 	uint8_t mfg_date[3];
463 	uint32_t board_sec_len;
464 	uint32_t i;
465 
466 	memset(msg_data, 0, 4);
467 	msg_data[0] = 0xb4;
468 	msg_data[1] = 0x90;
469 	msg_data[2] = 0x91;
470 	msg_data[3] = 0x8b;
471 
472 	memset(&req, 0, sizeof(req));
473 	req.msg.netfn = 0x3E;
474 	req.msg.cmd = 0x0E;
475 	req.msg.data = msg_data;
476 	req.msg.data_len = 4;
477 	/* Set Lun temporary, necessary for this oem command */
478 	req.msg.lun = 0x03;
479 	rsp = intf->sendrecv(intf, &req);
480 	if (rsp == NULL)  {
481 		printf("Device not present (No Response)\n");
482 		return(-1);
483 	} else if (rsp->ccode > 0) {
484 		printf("This option is not implemented for this board\n");
485 		return(-1);
486 	}
487 	if (rsp->data_len != 3) {
488 		printf("Invalid response for the Manufacturing date\n");
489 		return(-1);
490 	}
491 	memset(mfg_date, 0, 3);
492 	memcpy(mfg_date, rsp->data, 3);
493 	memset(msg_data, 0, 4);
494 	msg_data[0] = 0;
495 
496 	memset(&req, 0, sizeof(req));
497 	req.msg.netfn = IPMI_NETFN_STORAGE;
498 	req.msg.cmd = GET_FRU_INFO;
499 	req.msg.data = msg_data;
500 	req.msg.data_len = 1;
501 	rsp = intf->sendrecv(intf, &req);
502 	if (rsp == NULL) {
503 		printf(" Device not present (No Response)\n");
504 		return(-1);
505 	} else if (rsp->ccode > 0) {
506 		printf(" Device not present (%s)\n",
507 				val2str(rsp->ccode, completion_code_vals));
508 		return(-1);
509 	}
510 
511 	memset(&fru, 0, sizeof(fru));
512 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
513 	fru.access = rsp->data[2] & 0x1;
514 	if (fru.size < 1) {
515 		printf(" Invalid FRU size %d", fru.size);
516 		return(-1);
517 	}
518 	/* retrieve the FRU header */
519 	msg_data[0] = 0;
520 	msg_data[1] = 0;
521 	msg_data[2] = 0;
522 	msg_data[3] = 8;
523 
524 	memset(&req, 0, sizeof(req));
525 	req.msg.netfn = IPMI_NETFN_STORAGE;
526 	req.msg.cmd = GET_FRU_DATA;
527 	req.msg.data = msg_data;
528 	req.msg.data_len = 4;
529 	rsp = intf->sendrecv(intf, &req);
530 	if (rsp == NULL) {
531 		printf(" Device not present (No Response)\n");
532 		return (-1);
533 	} else if (rsp->ccode > 0) {
534 		printf(" Device not present (%s)\n",
535 				val2str(rsp->ccode, completion_code_vals));
536 		return (-1);
537 	}
538 	if (verbose > 1) {
539 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
540 	}
541 	memcpy(&header, rsp->data + 1, 8);
542 	if (header.version != 1) {
543 		printf(" Unknown FRU header version 0x%02x",
544 				header.version);
545 		return(-1);
546 	}
547 	board_sec_len = (header.offset.product * 8) - (header.offset.board * 8);
548 	fru_data = malloc(fru.size);
549 	if(fru_data == NULL) {
550 		lprintf(LOG_ERR, "ipmitool: malloc failure");
551 		return(-1);
552 	}
553 	memset(fru_data, 0, fru.size);
554 	if (read_fru_area(intf ,&fru ,0 ,(header.offset.board * 8),
555 				board_sec_len ,fru_data) < 0) {
556 		free(fru_data);
557 		fru_data = NULL;
558 		return(-1);
559 	}
560 	/* Copy the new manufacturing date in the board section saved in memory*/
561 	memcpy(fru_data + (header.offset.board * 8) + 3, mfg_date, 3);
562 	checksum = 0;
563 	/* Calculate Header Checksum */
564 	for (i = (header.offset.board * 8);
565 			i < (((header.offset.board * 8) + board_sec_len) - 2);
566 			i ++ ) {
567 		checksum += fru_data[i];
568 	}
569 	checksum = (~checksum) + 1;
570 	fru_data[(header.offset.board * 8)+board_sec_len - 1] = checksum;
571 	/* Write the new FRU Board section */
572 	if (write_fru_area(intf, &fru, 0, (header.offset.board * 8),
573 				(header.offset.board * 8),
574 				board_sec_len, fru_data) < 0) {
575 		free(fru_data);
576 		fru_data = NULL;
577 		return (-1);
578 	}
579 	free(fru_data);
580 	fru_data = NULL;
581 	return (1);
582 }
583 
584 static void
585 ipmi_kontron_nextboot_help(void)
586 {
587 	int i;
588 	printf("nextboot <device>\n"
589 			"Supported devices:\n");
590 	for (i = 0; bootdev[i] != 0; i++) {
591 		printf("- %s\n", bootdev[i]);
592 	}
593 }
594 
595 /* ipmi_kontron_next_boot_set - Select the next boot order on CP6012
596  *
597  * @intf: ipmi interface
598  * @id: fru id
599  *
600  * returns -1 on error
601  * returns 1 if successful
602  */
603 static int
604 ipmi_kontron_nextboot_set(struct ipmi_intf *intf, int argc, char **argv)
605 {
606 	struct ipmi_rs *rsp;
607 	struct ipmi_rq req;
608 	uint8_t msg_data[8];
609 	int i;
610 
611 	memset(msg_data, 0, sizeof(msg_data));
612 	msg_data[0] = 0xb4;
613 	msg_data[1] = 0x90;
614 	msg_data[2] = 0x91;
615 	msg_data[3] = 0x8b;
616 	msg_data[4] = 0x9d;
617 	msg_data[5] = 0xFF;
618 	msg_data[6] = 0xFF; /* any */
619 	for (i = 0; bootdev[i] != 0; i++) {
620 		if (strcmp(argv[0], bootdev[i]) == 0) {
621 			msg_data[5] = i;
622 			break;
623 		}
624 	}
625 	/* Invalid device selected? */
626 	if (msg_data[5] == 0xFF) {
627 		printf("Unknown boot device: %s\n", argv[0]);
628 		return (-1);
629 	}
630 	memset(&req, 0, sizeof(req));
631 	req.msg.netfn = 0x3E;
632 	req.msg.cmd = 0x02;
633 	req.msg.data = msg_data;
634 	req.msg.data_len = 7;
635 	/* Set Lun temporary, necessary for this oem command */
636 	req.msg.lun = 0x03;
637 	rsp = intf->sendrecv(intf, &req);
638 	if (rsp == NULL) {
639 		printf("Device not present (No Response)\n");
640 		return(-1);
641 	} else if (rsp->ccode > 0) {
642 		printf("Device not present (%s)\n",
643 				val2str(rsp->ccode, completion_code_vals));
644 		return (-1);
645 	}
646 	return 0;
647 }
648