xref: /openbmc/ipmitool/lib/ipmi_isol.c (revision 531569ec)
1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistribution of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * Redistribution in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * This software is provided "AS IS," without a warranty of any kind.
20  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <stdlib.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <stdio.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/select.h>
40 #include <sys/time.h>
41 #include <signal.h>
42 #include <unistd.h>
43 
44 
45 #include <termios.h>
46 
47 #include <ipmitool/helper.h>
48 #include <ipmitool/log.h>
49 #include <ipmitool/ipmi.h>
50 #include <ipmitool/ipmi_strings.h>
51 #include <ipmitool/ipmi_intf.h>
52 #include <ipmitool/ipmi_isol.h>
53 
54 static struct termios _saved_tio;
55 static int            _in_raw_mode = 0;
56 
57 extern int verbose;
58 
59 #define ISOL_ESCAPE_CHARACTER                    '~'
60 
61 /*
62  * ipmi_get_isol_info
63  */
64 static int ipmi_get_isol_info(struct ipmi_intf * intf,
65 			      struct isol_config_parameters * params)
66 {
67 	struct ipmi_rs * rsp;
68 	struct ipmi_rq req;
69 	unsigned char data[6];
70 
71 	memset(&req, 0, sizeof(req));
72 	req.msg.netfn = IPMI_NETFN_ISOL;
73 	req.msg.cmd = GET_ISOL_CONFIG;
74 	req.msg.data = data;
75 	req.msg.data_len = 4;
76 
77 	/* GET ISOL ENABLED CONFIG */
78 
79 	memset(data, 0, 6);
80 	data[0] = 0x00;
81 	data[1] = ISOL_ENABLE_PARAM;
82 	data[2] = 0x00;		/* block */
83 	data[3] = 0x00;		/* selector */
84 
85 	rsp = intf->sendrecv(intf, &req);
86 	if (rsp == NULL) {
87 		lprintf(LOG_ERR, "Error in Get ISOL Config Command");
88 		return -1;
89 	}
90 	if (rsp->ccode == 0xc1) {
91 		lprintf(LOG_ERR, "IPMI v1.5 Serial Over Lan (ISOL) not supported!");
92 		return -1;
93 	}
94 	if (rsp->ccode > 0) {
95 		lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s",
96 			val2str(rsp->ccode, completion_code_vals));
97 		return -1;
98 	}
99 	params->enabled = rsp->data[1];
100 
101 	/* GET ISOL AUTHENTICATON CONFIG */
102 
103 	memset(data, 0, 6);
104 	data[0] = 0x00;
105 	data[1] = ISOL_AUTHENTICATION_PARAM;
106 	data[2] = 0x00;		/* block */
107 	data[3] = 0x00;		/* selector */
108 
109 	rsp = intf->sendrecv(intf, &req);
110 	if (rsp == NULL) {
111 		lprintf(LOG_ERR, "Error in Get ISOL Config Command");
112 		return -1;
113 	}
114 	if (rsp->ccode > 0) {
115 		lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s",
116 			val2str(rsp->ccode, completion_code_vals));
117 		return -1;
118 	}
119 	params->privilege_level = rsp->data[1];
120 
121 	/* GET ISOL BAUD RATE CONFIG */
122 
123 	memset(data, 0, 6);
124 	data[0] = 0x00;
125 	data[1] = ISOL_BAUD_RATE_PARAM;
126 	data[2] = 0x00;		/* block */
127 	data[3] = 0x00;		/* selector */
128 
129 	rsp = intf->sendrecv(intf, &req);
130 	if (rsp == NULL) {
131 		lprintf(LOG_ERR, "Error in Get ISOL Config Command");
132 		return -1;
133 	}
134 	if (rsp->ccode > 0) {
135 		lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s",
136 			val2str(rsp->ccode, completion_code_vals));
137 		return -1;
138 	}
139 	params->bit_rate = rsp->data[1];
140 
141 	return 0;
142 }
143 
144 static int ipmi_print_isol_info(struct ipmi_intf * intf)
145 {
146 	struct isol_config_parameters params = {0};
147 	if (ipmi_get_isol_info(intf, &params))
148 		return -1;
149 
150 	if (csv_output)
151 	{
152 		printf("%s,", (params.enabled & 0x1)?"true": "false");
153 		printf("%s,",
154 			   val2str((params.privilege_level & 0xf), ipmi_privlvl_vals));
155 		printf("%s,",
156 			   val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals));
157 	}
158 	else
159 	{
160 		printf("Enabled                         : %s\n",
161 		       (params.enabled & 0x1)?"true": "false");
162 		printf("Privilege Level                 : %s\n",
163 		       val2str((params.privilege_level & 0xf), ipmi_privlvl_vals));
164 		printf("Bit Rate (kbps)                 : %s\n",
165 		       val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals));
166 	}
167 
168 	return 0;
169 }
170 
171 static int ipmi_isol_set_param(struct ipmi_intf * intf,
172 			       const char *param,
173 			       const char *value)
174 {
175 	struct ipmi_rs * rsp;
176 	struct ipmi_rq req;
177 	unsigned char data[6];
178 	struct isol_config_parameters params = {0};
179 
180 	/* We need other values to complete the request */
181 	if (ipmi_get_isol_info(intf, &params))
182 		return -1;
183 
184 	memset(&req, 0, sizeof(req));
185 	req.msg.netfn = IPMI_NETFN_ISOL;
186 	req.msg.cmd = SET_ISOL_CONFIG;
187 	req.msg.data = data;
188 	req.msg.data_len = 3;
189 
190 	memset(data, 0, 6);
191 
192 	/*
193 	 * enabled
194 	 */
195 	if (strcmp(param, "enabled") == 0)
196 	{
197 		data[1] = ISOL_ENABLE_PARAM;
198 		if (strcmp(value, "true") == 0)
199 			data[2] = 0x01;
200 		else if (strcmp(value, "false") == 0)
201 			data[2] = 0x00;
202 		else {
203 			lprintf(LOG_ERR, "Invalid value %s for parameter %s",
204 				   value, param);
205 			lprintf(LOG_ERR, "Valid values are true and false");
206 			return -1;
207 		}
208 	}
209 
210 	/*
211 	 * privilege-level
212 	 */
213 	else if (strcmp(param, "privilege-level") == 0)
214 	{
215 		data[1] = ISOL_AUTHENTICATION_PARAM;
216 		if (! strcmp(value, "user"))
217 			data[2] = 0x02;
218 		else if (! strcmp(value, "operator"))
219 			data[2] = 0x03;
220 		else if (! strcmp(value, "admin"))
221 			data[2] = 0x04;
222 		else if (! strcmp(value, "oem"))
223 			data[2] = 0x05;
224 		else
225 		{
226 			lprintf(LOG_ERR, "Invalid value %s for parameter %s",
227 				   value, param);
228 			lprintf(LOG_ERR, "Valid values are user, operator, admin, and oem");
229 			return -1;
230 		}
231 		/* We need to mask bit7 from the fetched value */
232 		data[2] |= (params.privilege_level & 0x80) ? 0x80 : 0x00;
233 	}
234 
235 	/*
236 	 * bit-rate
237 	 */
238 	else if (strcmp(param, "bit-rate") == 0)
239 	{
240 		data[1] = ISOL_BAUD_RATE_PARAM;
241 		if (strncmp(value, "9.6", 3) == 0) {
242 			data[2] = 0x06;
243 		}
244 		else if (strncmp(value, "19.2", 4) == 0) {
245 			data[2] = 0x07;
246 		}
247 		else if (strncmp(value, "38.4", 4) == 0) {
248 			data[2] = 0x08;
249 		}
250 		else if (strncmp(value, "57.6", 4) == 0) {
251 			data[2] = 0x09;
252 		}
253 		else if (strncmp(value, "115.2", 5) == 0) {
254 			data[2] = 0x0A;
255 		}
256 		else {
257 			lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", value);
258 			lprintf(LOG_ERR, "Valid values are 9.6, 19.2, 38.4, 57.6 and 115.2");
259 			return -1;
260 		}
261 	}
262 	else
263 	{
264 		lprintf(LOG_ERR, "Error: invalid ISOL parameter %s", param);
265 		return -1;
266 	}
267 
268 
269 	/*
270 	 * Execute the request
271 	 */
272 
273 	rsp = intf->sendrecv(intf, &req);
274 	if (rsp == NULL) {
275 		lprintf(LOG_ERR, "Error setting ISOL parameter '%s'", param);
276 		return -1;
277 	}
278 	if (rsp->ccode > 0) {
279 		lprintf(LOG_ERR, "Error setting ISOL parameter '%s': %s",
280 			   param, val2str(rsp->ccode, completion_code_vals));
281 		return -1;
282 	}
283 
284 	return 0;
285 }
286 
287 static void
288 leave_raw_mode(void)
289 {
290 	if (!_in_raw_mode)
291 		return;
292 	if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1)
293 		perror("tcsetattr");
294 	else
295 		_in_raw_mode = 0;
296 }
297 
298 
299 
300 static void
301 enter_raw_mode(void)
302 {
303 	struct termios tio;
304 	if (tcgetattr(fileno(stdin), &tio) == -1) {
305 		perror("tcgetattr");
306 		return;
307 	}
308 	_saved_tio = tio;
309 	tio.c_iflag |= IGNPAR;
310 	tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF)\
311 		;
312 	tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
313 	//	#ifdef IEXTEN
314 	tio.c_lflag &= ~IEXTEN;
315 	//	#endif
316 	tio.c_oflag &= ~OPOST;
317 	tio.c_cc[VMIN] = 1;
318 	tio.c_cc[VTIME] = 0;
319 	if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1)
320 		perror("tcsetattr");
321 	else
322 		_in_raw_mode = 1;
323 }
324 
325 
326 static void
327 sendBreak(struct ipmi_intf * intf)
328 {
329 	struct ipmi_v2_payload  v2_payload;
330 
331 	memset(&v2_payload, 0, sizeof(v2_payload));
332 
333 	v2_payload.payload.sol_packet.character_count = 0;
334 	v2_payload.payload.sol_packet.generate_break  = 1;
335 
336 	intf->send_sol(intf, &v2_payload);
337 }
338 
339 /*
340  * suspendSelf
341  *
342  * Put ourself in the background
343  *
344  * param bRestoreTty specifies whether we will put our self back
345  *       in raw mode when we resume
346  */
347 static void
348 suspendSelf(int bRestoreTty)
349 {
350 	leave_raw_mode();
351 	kill(getpid(), SIGTSTP);
352 
353 	if (bRestoreTty)
354 		enter_raw_mode();
355 }
356 
357 
358 
359 /*
360  * printiSolEscapeSequences
361  *
362  * Send some useful documentation to the user
363  */
364 static void
365 printiSolEscapeSequences(void)
366 {
367 	printf(
368 		   "%c?\n\
369 	Supported escape sequences:\n\
370 	%c.  - terminate connection\n\
371 	%c^Z - suspend ipmitool\n\
372 	%c^X - suspend ipmitool, but don't restore tty on restart\n\
373 	%cB  - send break\n\
374 	%c?  - this message\n\
375 	%c%c  - send the escape character by typing it twice\n\
376 	(Note that escapes are only recognized immediately after newline.)\n",
377 		   ISOL_ESCAPE_CHARACTER,
378 		   ISOL_ESCAPE_CHARACTER,
379 		   ISOL_ESCAPE_CHARACTER,
380 		   ISOL_ESCAPE_CHARACTER,
381 		   ISOL_ESCAPE_CHARACTER,
382 		   ISOL_ESCAPE_CHARACTER,
383 		   ISOL_ESCAPE_CHARACTER,
384 		   ISOL_ESCAPE_CHARACTER);
385 }
386 
387 
388 
389 /*
390  * output
391  *
392  * Send the specified data to stdout
393  */
394 static void
395 output(struct ipmi_rs * rsp)
396 {
397 	if (rsp)
398 	{
399 		int i;
400 		for (i = 0; i < rsp->data_len; ++i)
401 			putc(rsp->data[i], stdout);
402 
403 		fflush(stdout);
404 	}
405 }
406 
407 /*
408  * ipmi_isol_deactivate
409  */
410 static int
411 ipmi_isol_deactivate(struct ipmi_intf * intf)
412 {
413 	struct ipmi_rs * rsp;
414 	struct ipmi_rq   req;
415 	uint8_t    data[6];
416 
417 	memset(&req, 0, sizeof(req));
418 	req.msg.netfn = IPMI_NETFN_ISOL;
419 	req.msg.cmd = ACTIVATE_ISOL;
420 	req.msg.data = data;
421 	req.msg.data_len = 5;
422 
423 	memset(data, 0, 6);
424 	data[0] = 0x00; /* Deactivate */
425 	data[1] = 0x00;
426 	data[2] = 0x00;
427 	data[3] = 0x00;
428 	data[5] = 0x00;
429 
430 	rsp = intf->sendrecv(intf, &req);
431 	if (rsp == NULL) {
432 		lprintf(LOG_ERR, "Error deactivating ISOL");
433 		return -1;
434 	}
435 	if (rsp->ccode > 0) {
436 		lprintf(LOG_ERR, "Error deactivating ISOL: %s",
437 			val2str(rsp->ccode, completion_code_vals));
438 		return -1;
439 	}
440 	/* response contain 4 additional bytes : 80 00 32 ff
441 	   Don't know what to use them for yet... */
442 	return 0;
443 }
444 
445 /*
446  * processiSolUserInput
447  *
448  * Act on user input into the ISOL session.  The only reason this
449  * is complicated is that we have to process escape sequences.
450  *
451  * return   0 on success
452  *          1 if we should exit
453  *        < 0 on error (BMC probably closed the session)
454  */
455 static int
456 processiSolUserInput(struct ipmi_intf * intf,
457 		    uint8_t * input,
458 		    uint16_t  buffer_length)
459 {
460 	static int escape_pending = 0;
461 	static int last_was_cr    = 1;
462 	struct ipmi_v2_payload v2_payload;
463 	int  length               = 0;
464 	int  retval               = 0;
465 	char ch;
466 	int  i;
467 
468 	memset(&v2_payload, 0, sizeof(v2_payload));
469 
470 	/*
471 	 * Our first order of business is to check the input for escape
472 	 * sequences to act on.
473 	 */
474 	for (i = 0; i < buffer_length; ++i)
475 	{
476 		ch = input[i];
477 
478 		if (escape_pending){
479 			escape_pending = 0;
480 
481 			/*
482 			 * Process a possible escape sequence.
483 			 */
484 			switch (ch) {
485 			case '.':
486 				printf("%c. [terminated ipmitool]\n", ISOL_ESCAPE_CHARACTER);
487 				retval = 1;
488 				break;
489 			case 'Z' - 64:
490 				printf("%c^Z [suspend ipmitool]\n", ISOL_ESCAPE_CHARACTER);
491 				suspendSelf(1); /* Restore tty back to raw */
492 				continue;
493 
494 			case 'X' - 64:
495 				printf("%c^X [suspend ipmitool]\n", ISOL_ESCAPE_CHARACTER);
496 				suspendSelf(0); /* Don't restore to raw mode */
497 				continue;
498 
499 			case 'B':
500 				printf("%cb [send break]\n", ISOL_ESCAPE_CHARACTER);
501 				sendBreak(intf);
502 				continue;
503 
504 			case '?':
505 				printiSolEscapeSequences();
506 				continue;
507 			default:
508 				if (ch != ISOL_ESCAPE_CHARACTER)
509 					v2_payload.payload.sol_packet.data[length++] =
510 						ISOL_ESCAPE_CHARACTER;
511 				v2_payload.payload.sol_packet.data[length++] = ch;
512 			}
513 		}
514 
515 		else
516 		{
517 			if (last_was_cr && (ch == ISOL_ESCAPE_CHARACTER)) {
518 				escape_pending = 1;
519 				continue;
520 			}
521 
522 			v2_payload.payload.sol_packet.data[length++] =	ch;
523 		}
524 
525 
526 		/*
527 		 * Normal character.  Record whether it was a newline.
528 		 */
529 		last_was_cr = (ch == '\r' || ch == '\n');
530 	}
531 
532 	/*
533 	 * If there is anything left to process we dispatch it to the BMC,
534 	 * send intf->session->sol_data.max_outbound_payload_size bytes
535 	 * at a time.
536 	 */
537 	if (length)
538 	{
539 		struct ipmi_rs * rsp;
540 
541 		v2_payload.payload.sol_packet.flush_outbound = 1; /* Not sure if necessary ? */
542 		v2_payload.payload.sol_packet.character_count = length;
543 		rsp = intf->send_sol(intf, &v2_payload);
544 
545 		if (! rsp) {
546 			lprintf(LOG_ERR, "Error sending SOL data");
547 			retval = -1;
548 		}
549 
550 		/* If the sequence number is set we know we have new data */
551 		else if ((rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)        &&
552 			 (rsp->payload.sol_packet.packet_sequence_number))
553 			output(rsp);
554 	}
555 	return retval;
556 }
557 
558 /*
559  * ipmi_isol_red_pill
560  */
561 static int
562 ipmi_isol_red_pill(struct ipmi_intf * intf)
563 {
564 	char   * buffer;
565 	int    numRead;
566 	int    bShouldExit       = 0;
567 	int    bBmcClosedSession = 0;
568 	fd_set read_fds;
569 	struct timeval tv;
570 	int    retval;
571 	int    buffer_size = 255;
572 	int    timedout = 0;
573 
574 	buffer = (char*)malloc(buffer_size);
575 	if (buffer == NULL) {
576 		lprintf(LOG_ERR, "ipmitool: malloc failure");
577 		return -1;
578 	}
579 
580 	enter_raw_mode();
581 
582 	while (! bShouldExit)
583 	{
584 		FD_ZERO(&read_fds);
585 		FD_SET(0, &read_fds);
586 		FD_SET(intf->fd, &read_fds);
587 
588 		/* Wait up to half a second */
589 		tv.tv_sec =  0;
590 		tv.tv_usec = 500000;
591 
592 		retval = select(intf->fd + 1, &read_fds, NULL, NULL, &tv);
593 
594 		if (retval)
595 		{
596 			if (retval == -1)
597 			{
598 				/* ERROR */
599 				perror("select");
600 				return -1;
601 			}
602 
603 			timedout = 0;
604 
605 			/*
606 			 * Process input from the user
607 			 */
608 			if (FD_ISSET(0, &read_fds))
609 	 		{
610 				memset(buffer, 0, buffer_size);
611 				numRead = read(fileno(stdin),
612 							   buffer,
613 							   buffer_size);
614 
615 				if (numRead > 0)
616 				{
617 					int rc = processiSolUserInput(intf, buffer, numRead);
618 
619 					if (rc)
620 					{
621 						if (rc < 0)
622 							bShouldExit = bBmcClosedSession = 1;
623 						else
624 							bShouldExit = 1;
625 					}
626 				}
627 				else
628 				{
629 					bShouldExit = 1;
630 				}
631 			}
632 
633 
634 			/*
635 			 * Process input from the BMC
636 			 */
637 			else if (FD_ISSET(intf->fd, &read_fds))
638 			{
639 				struct ipmi_rs * rs = intf->recv_sol(intf);
640 				if (! rs)
641 				{
642 					bShouldExit = bBmcClosedSession = 1;
643 				}
644 				else
645 					output(rs);
646  			}
647 
648 
649 			/*
650 			 * ERROR in select
651 			 */
652  			else
653 			{
654 				lprintf(LOG_ERR, "Error: Select returned with nothing to read");
655 				bShouldExit = 1;
656 			}
657 		}
658 		else
659 		{
660 			if ((++timedout) == 20) /* Every 10 seconds we send a keepalive */
661 			{
662 				intf->keepalive(intf);
663 				timedout = 0;
664 			}
665 		}
666 	}
667 
668 	leave_raw_mode();
669 
670 	if (bBmcClosedSession)
671 	{
672 		lprintf(LOG_ERR, "SOL session closed by BMC");
673 	}
674 	else
675 		ipmi_isol_deactivate(intf);
676 
677 	return 0;
678 }
679 
680 /*
681  * ipmi_isol_activate
682  */
683 static int
684 ipmi_isol_activate(struct ipmi_intf * intf)
685 {
686 	struct ipmi_rs * rsp;
687 	struct ipmi_rq   req;
688 	uint8_t    data[6];
689 	struct isol_config_parameters params;
690 
691 	if (ipmi_get_isol_info(intf, &params))
692 		return -1;
693 
694 	if (!(params.enabled & 0x1)) {
695 		lprintf(LOG_ERR, "ISOL is not enabled!");
696 		return -1;
697 	}
698 
699 	/*
700 	 * Setup a callback so that the lanplus processing knows what
701 	 * to do with packets that come unexpectedly (while waiting for
702 	 * an ACK, perhaps.
703 	 */
704 	intf->session->sol_data.sol_input_handler = output;
705 
706 	memset(&req, 0, sizeof(req));
707 	req.msg.netfn = IPMI_NETFN_ISOL;
708 	req.msg.cmd = ACTIVATE_ISOL;
709 	req.msg.data = data;
710 	req.msg.data_len = 5;
711 
712 	memset(data, 0, 6);
713 	data[0] = 0x01;
714 	data[1] = 0x00;
715 	data[2] = 0x00;
716 	data[3] = 0x00;
717 	data[5] = 0x00;
718 
719 	rsp = intf->sendrecv(intf, &req);
720 	if (NULL != rsp) {
721 		switch (rsp->ccode) {
722 			case 0x00:
723 				if (rsp->data_len == 4) {
724 					break;
725 				} else {
726 					lprintf(LOG_ERR, "Error: Unexpected data length (%d) received "
727 						   "in ISOL activation response",
728 						   rsp->data_len);
729 					return -1;
730 				}
731 				break;
732 			case 0x80:
733 				lprintf(LOG_ERR, "Info: ISOL already active on another session");
734 				return -1;
735 			case 0x81:
736 				lprintf(LOG_ERR, "Info: ISOL disabled");
737 				return -1;
738 			case 0x82:
739 				lprintf(LOG_ERR, "Info: ISOL activation limit reached");
740 				return -1;
741 			default:
742 				lprintf(LOG_ERR, "Error activating ISOL: %s",
743 					val2str(rsp->ccode, completion_code_vals));
744 				return -1;
745 		}
746 	} else {
747 		lprintf(LOG_ERR, "Error: No response activating ISOL");
748 		return -1;
749 	}
750 
751 	/* response contain 4 additional bytes : 80 01 32 ff
752 	   Don't know what to use them for yet... */
753 
754 	printf("[SOL Session operational.  Use %c? for help]\n",
755 	       ISOL_ESCAPE_CHARACTER);
756 
757 	/*
758 	 * At this point we are good to go with our SOL session.  We
759 	 * need to listen to
760 	 * 1) STDIN for user input
761 	 * 2) The FD for incoming SOL packets
762 	 */
763 	if (ipmi_isol_red_pill(intf)) {
764 		lprintf(LOG_ERR, "Error in SOL session");
765 		return -1;
766 	}
767 
768 	return 0;
769 }
770 
771 static void print_isol_set_usage(void) {
772 	lprintf(LOG_NOTICE, "\nISOL set parameters and values: \n");
773 	lprintf(LOG_NOTICE, "  enabled                     true | false");
774 	lprintf(LOG_NOTICE, "  privilege-level             user | operator | admin | oem");
775 	lprintf(LOG_NOTICE, "  bit-rate                    "
776 		"9.6 | 19.2 | 38.4 | 57.6 | 115.2");
777 	lprintf(LOG_NOTICE, "");
778 }
779 
780 static void print_isol_usage(void) {
781 	lprintf(LOG_NOTICE, "ISOL Commands: info");
782 	lprintf(LOG_NOTICE, "               set <parameter> <setting>");
783 	lprintf(LOG_NOTICE, "               activate");
784 }
785 
786 int ipmi_isol_main(struct ipmi_intf * intf, int argc, char ** argv)
787 {
788 	int ret = 0;
789 
790 	/*
791 	 * Help
792 	 */
793 	if (!argc || !strncmp(argv[0], "help", 4))
794 		print_isol_usage();
795 
796 	/*
797 	 * Info
798 	 */
799 	else if (!strncmp(argv[0], "info", 4)) {
800 		ret = ipmi_print_isol_info(intf);
801 	}
802 
803 	/*
804 	 * Set a parameter value
805 	 */
806 	else if (!strncmp(argv[0], "set", 3)) {
807 		if (argc < 3) {
808 			print_isol_set_usage();
809 			return -1;
810 		}
811 		ret = ipmi_isol_set_param(intf, argv[1], argv[2]);
812 	}
813 
814 	/*
815 	 * Activate
816 	 */
817  	else if (!strncmp(argv[0], "activate", 8)) {
818 		ret = ipmi_isol_activate(intf);
819 	}
820 
821 	else {
822 		print_isol_usage();
823 		ret = -1;
824 	}
825 
826 	return ret;
827 }
828