xref: /titanic_44/usr/src/lib/libpcp/common/libpcp.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Platform Channel Protocol Library functions on Nigara platforms
29  * (Ontario, Erie, etc..) Solaris applications use these interfaces
30  * to communicate with entities that reside on service processor.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <assert.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <signal.h>
43 #include <setjmp.h>
44 #include <inttypes.h>
45 #include <umem.h>
46 #include <strings.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/glvc.h>
50 #include <netinet/in.h>
51 
52 #include "libpcp.h"
53 #include "pcp_common.h"
54 
55 /*
56  * Following libpcp interfaces are exposed to user applications.
57  *
58  * int pcp_init(char *channel_dev_path);
59  * int pcp_send_recv(pcp_msg_t *req_msg, pcp_msg_t *resp_msg, uint32_t timeout);
60  * int pcp_close(void);
61  *
62  */
63 
64 /*
65  * Forward declarations.
66  */
67 static int pcp_send_req_msg_hdr(pcp_req_msg_hdr_t *req_hdr);
68 static int pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr);
69 static int pcp_io_op(void *buf, int byte_cnt, int io_op);
70 static int pcp_get_xid(void);
71 static int pcp_get_prop(int prop, unsigned int *val);
72 static int pcp_read(uint8_t *buf, int buf_len);
73 static int pcp_write(uint8_t *buf, int buf_len);
74 static int pcp_peek(uint8_t *buf, int buf_len);
75 static int pcp_peek_read(uint8_t *buf, int buf_len);
76 static int pcp_frame_error_handle(void);
77 static int check_magic_byte_presence(int byte_cnt, uint8_t *byte_val,
78 					int *ispresent);
79 static uint16_t checksum(uint16_t *addr, int32_t count);
80 static int pcp_cleanup(void);
81 
82 /*
83  * glvc driver file descriptor
84  */
85 static int chnl_fd = -1;
86 
87 /*
88  * Message Transaction ID
89  */
90 static uint32_t msg_xid = 0;
91 
92 /*
93  * Channel MTU size.
94  */
95 static unsigned int mtu_size = PCPL_DEF_MTU_SZ;
96 
97 /*
98  * timeout field is supplied by user. timeout field is used to decide
99  * how long to block on glvc driver calls before we return timeout error
100  * to user applications.
101  *
102  * Note: In the current implementation of glvc driver, all glvc calls are
103  *       blocking.
104  */
105 static uint32_t glvc_timeout = 0;
106 
107 /*
108  * variables used by setsetjmp/siglongjmp.
109  */
110 static volatile sig_atomic_t jumpok = 0;
111 static sigjmp_buf jmpbuf;
112 
113 /*
114  * To unblock SIGALRM signal incase if it's blocked in libpcp user apps.
115  * Restore it to old state during pcp_close.
116  */
117 static sigset_t blkset;
118 
119 /*
120  * Buffers used for stream reading channel data. When data is read in
121  * stream fashion, first data is copied from channel (glvc) buffers to
122  * these local buffers from which the read requests are serviced.
123  */
124 #define	READ_AREA_SIZE	(2*mtu_size)
125 static uint8_t *read_head = NULL;
126 static uint8_t *read_tail = NULL;
127 static uint8_t *read_area = NULL;
128 
129 /*
130  * Buffer used for peeking new data available in channel (glvc) buffers.
131  */
132 #define	PEEK_AREA_SIZE	(mtu_size)
133 static uint8_t *peek_area = NULL;
134 
135 /*
136  * Buffers used for peeking data available either in local buffers or
137  * new data available in channel (glvc) buffers.
138  */
139 #define	PEEK_READ_AREA_SIZE	(2*mtu_size)
140 static uint8_t *peek_read_head = NULL;
141 static uint8_t *peek_read_tail = NULL;
142 static uint8_t *peek_read_area = NULL;
143 
144 static pcp_req_msg_hdr_t *req_msg_hdr = NULL;
145 static pcp_resp_msg_hdr_t *resp_msg_hdr = NULL;
146 static int req_msg_hdr_sz = 0;
147 static int resp_msg_hdr_sz = 0;
148 
149 /*
150  * signal handling variables to handle glvc blocking calls.
151  */
152 static struct sigaction glvc_act;
153 
154 /* To restore old SIGALRM signal handler */
155 static struct sigaction old_act;
156 
157 static void
158 glvc_timeout_handler(void)
159 {
160 	if (jumpok == 0)
161 		return;
162 	siglongjmp(jmpbuf, 1);
163 }
164 
165 /*
166  * Initialize the virtual channel. It basically opens the virtual channel
167  * provided by the host application.
168  *
169  */
170 
171 int
172 pcp_init(char *channel_dev_path)
173 {
174 	sigset_t oldset;
175 
176 	if (channel_dev_path == NULL)
177 		return (PCPL_INVALID_ARGS);
178 	/*
179 	 * Open virtual channel device node.
180 	 */
181 	if ((chnl_fd = open(channel_dev_path, O_RDWR|O_EXCL)) < 0) {
182 		return (PCPL_GLVC_ERROR);
183 	}
184 
185 	/*
186 	 * Initialize Message Transaction ID.
187 	 */
188 	msg_xid = PCPL_INIT_XID;
189 
190 	/*
191 	 * Get the Channel MTU size
192 	 */
193 
194 	if (pcp_get_prop(GLVC_XPORT_OPT_MTU_SZ, &mtu_size) != 0) {
195 		(void) close(chnl_fd);
196 		chnl_fd = -1;
197 		return (PCPL_GLVC_ERROR);
198 	}
199 
200 	/*
201 	 * Get current signal mask. If SIGALRM is blocked
202 	 * unblock it.
203 	 */
204 	(void) sigprocmask(0, NULL, &oldset);
205 
206 	(void) sigemptyset(&blkset);
207 
208 	if (sigismember(&oldset, SIGALRM)) {
209 		(void) sigaddset(&blkset, SIGALRM);
210 		(void) sigprocmask(SIG_UNBLOCK, &blkset, NULL);
211 	}
212 	/*
213 	 * signal handler initialization to handle glvc call timeouts.
214 	 */
215 	glvc_act.sa_handler = glvc_timeout_handler;
216 	(void) sigemptyset(&glvc_act.sa_mask);
217 	glvc_act.sa_flags = SA_NODEFER;
218 
219 	if (sigaction(SIGALRM, &glvc_act, &old_act) < 0) {
220 		(void) close(chnl_fd);
221 		chnl_fd = -1;
222 		return (PCPL_ERROR);
223 	}
224 
225 	return (PCPL_OK);
226 }
227 
228 /*
229  * Function: Close platform channel.
230  * Arguments: None
231  * Returns:
232  *	always returns PCPL_OK for now.
233  */
234 int
235 pcp_close(void)
236 {
237 
238 	if (chnl_fd >= 0) {
239 		(void) pcp_cleanup();
240 		(void) close(chnl_fd);
241 		chnl_fd = -1;
242 	}
243 
244 	/*
245 	 * free global buffers
246 	 */
247 	if (read_area != NULL) {
248 		umem_free(read_area, READ_AREA_SIZE);
249 		read_area = NULL;
250 	}
251 	if (peek_area != NULL) {
252 		umem_free(peek_area, PEEK_AREA_SIZE);
253 		peek_area = NULL;
254 	}
255 	if (peek_read_area != NULL) {
256 		umem_free(peek_read_area, PEEK_READ_AREA_SIZE);
257 		peek_read_area = NULL;
258 	}
259 	if (req_msg_hdr != NULL) {
260 		umem_free(req_msg_hdr, req_msg_hdr_sz);
261 		req_msg_hdr = NULL;
262 	}
263 	if (resp_msg_hdr != NULL) {
264 		umem_free(resp_msg_hdr, resp_msg_hdr_sz);
265 		resp_msg_hdr = NULL;
266 	}
267 
268 	/*
269 	 * Restore SIGALRM signal mask incase if we unblocked
270 	 * it during pcp_init.
271 	 */
272 	if (sigismember(&blkset, SIGALRM)) {
273 		(void) sigprocmask(SIG_BLOCK, &blkset, NULL);
274 	}
275 
276 	/* Restore SIGALRM signal handler */
277 	(void) sigaction(SIGALRM, &old_act, NULL);
278 
279 	return (PCPL_OK);
280 }
281 
282 /*
283  * Function: Send and Receive messages on platform channel.
284  * Arguments:
285  *	pcp_msg_t *req_msg  - Request Message to send to other end of channel.
286  *	pcp_msg_t *resp_msg - Response Message to be received.
287  *	uint32_t timeout    - timeout field when waiting for data from channel.
288  * Returns:
289  *	0  - success (PCPL_OK).
290  *	(-ve) - failure:
291  *			PCPL_INVALID_ARGS - invalid args.
292  *			PCPL_GLVC_TIMEOUT - glvc call timeout.
293  *			PCPL_XPORT_ERROR - transport error in request message
294  *						noticed by receiver.
295  *			PCPL_MALLOC_FAIL - malloc failure.
296  *			PCPL_CKSUM_ERROR - checksum error.
297  */
298 int
299 pcp_send_recv(pcp_msg_t *req_msg, pcp_msg_t *resp_msg, uint32_t timeout)
300 {
301 	void *datap;
302 	void *resp_msg_data = NULL;
303 	uint32_t status;
304 	uint16_t cksum = 0;
305 	int ret;
306 	int resp_hdr_ok;
307 #ifdef PCP_CKSUM_ENABLE
308 	uint16_t bkup_resp_hdr_cksum;
309 #endif
310 	if (chnl_fd < 0) {
311 		return (PCPL_ERROR);
312 	}
313 
314 	if (req_msg == NULL) {
315 		return (PCPL_INVALID_ARGS);
316 	}
317 
318 	if (timeout > 0)
319 		glvc_timeout = timeout;
320 	else
321 		glvc_timeout = 0;
322 
323 	if ((datap = req_msg->msg_data) == NULL)
324 		return (PCPL_INVALID_ARGS);
325 
326 	if (req_msg_hdr == NULL) {
327 		req_msg_hdr_sz = sizeof (pcp_req_msg_hdr_t);
328 		req_msg_hdr = (pcp_req_msg_hdr_t *)umem_zalloc(req_msg_hdr_sz,
329 								UMEM_DEFAULT);
330 		if (req_msg_hdr == NULL)
331 			return (PCPL_MALLOC_FAIL);
332 	}
333 
334 	/* calculate request msg_cksum */
335 	cksum = checksum((uint16_t *)datap, req_msg->msg_len);
336 
337 	/*
338 	 * Fill in the message header for the request packet
339 	 */
340 	req_msg_hdr->magic_num = PCP_MAGIC_NUM;
341 	req_msg_hdr->proto_ver = PCP_PROT_VER_1;
342 	req_msg_hdr->msg_type = req_msg->msg_type;
343 	req_msg_hdr->sub_type = req_msg->sub_type;
344 	req_msg_hdr->rsvd_pad = 0;
345 	req_msg_hdr->xid = pcp_get_xid();
346 	req_msg_hdr->msg_len  = req_msg->msg_len;
347 	req_msg_hdr->timeout = timeout;
348 	req_msg_hdr->msg_cksum = cksum;
349 	req_msg_hdr->hdr_cksum = 0;
350 
351 	/* fill request header checksum */
352 	req_msg_hdr->hdr_cksum = checksum((uint16_t *)req_msg_hdr,
353 					req_msg_hdr_sz);
354 	/*
355 	 * set sig jmp location
356 	 */
357 	if (sigsetjmp(jmpbuf, 1)) {
358 		return (PCPL_GLVC_TIMEOUT);
359 	}
360 	jumpok = 1; /* monitor sigalrm from now on */
361 
362 	/*
363 	 * send request message header
364 	 */
365 	if ((ret = pcp_send_req_msg_hdr(req_msg_hdr))) {
366 
367 		return (ret);
368 	}
369 
370 	/*
371 	 * send request message
372 	 */
373 	if ((ret = pcp_io_op(datap, req_msg->msg_len,
374 					PCPL_IO_OP_WRITE))) {
375 		return (ret);
376 	}
377 	if (timeout == (uint32_t)PCP_TO_NO_RESPONSE)
378 		return (PCPL_OK);
379 
380 	if (resp_msg_hdr == NULL) {
381 		resp_msg_hdr_sz = sizeof (pcp_resp_msg_hdr_t);
382 		resp_msg_hdr = (pcp_resp_msg_hdr_t *)umem_alloc(resp_msg_hdr_sz,
383 								UMEM_DEFAULT);
384 		if (resp_msg_hdr == NULL)
385 			return (PCPL_MALLOC_FAIL);
386 	}
387 
388 	resp_hdr_ok = 0;
389 	while (!resp_hdr_ok) {
390 
391 		/*
392 		 * Receive response message header
393 		 * Note: frame error handling is done in
394 		 * 'pcp_recv_resp_msg_hdr()'.
395 		 */
396 		if ((ret = pcp_recv_resp_msg_hdr(resp_msg_hdr))) {
397 			return (ret);
398 		}
399 
400 		/*
401 		 * Check header checksum if it matches with the received hdr
402 		 * checksum.
403 		 */
404 #ifdef PCP_CKSUM_ENABLE
405 		bkup_resp_hdr_cksum = resp_msg_hdr->hdr_cksum;
406 		resp_msg_hdr->hdr_cksum = 0;
407 		cksum = checksum((uint16_t *)resp_msg_hdr, resp_msg_hdr_sz);
408 
409 		if (cksum != bkup_resp_hdr_cksum) {
410 			return (PCPL_CKSUM_ERROR);
411 		}
412 #endif
413 		/*
414 		 * Check for matching request and response messages
415 		 */
416 		if (resp_msg_hdr->xid != req_msg_hdr->xid) {
417 
418 			continue; /* continue reading response header */
419 		}
420 		resp_hdr_ok = 1;
421 	}
422 
423 	/*
424 	 * check status field for any channel protocol errrors
425 	 * This field signifies something happend during request
426 	 * message trasmission. This field is set by the receiver.
427 	 */
428 	status = resp_msg_hdr->status;
429 	if (status != PCP_OK) {
430 		return (PCPL_XPORT_ERROR);
431 	}
432 
433 
434 	/* libpcp users should free this memory */
435 	if ((resp_msg_data = (uint8_t *)malloc(resp_msg_hdr->msg_len)) == NULL)
436 		return (PCPL_MALLOC_FAIL);
437 	bzero(resp_msg_data, resp_msg_hdr->msg_len);
438 	/*
439 	 * Receive response message.
440 	 */
441 	if ((ret = pcp_io_op(resp_msg_data, resp_msg_hdr->msg_len,
442 						PCPL_IO_OP_READ))) {
443 		free(resp_msg_data);
444 		return (ret);
445 	}
446 
447 #ifdef PCP_CKSUM_ENABLE
448 	/* verify response message data checksum */
449 	cksum = checksum((uint16_t *)resp_msg_data,
450 				resp_msg_hdr->msg_len);
451 	if (cksum != resp_msg_hdr->msg_cksum) {
452 		free(resp_msg_data);
453 		return (PCPL_CKSUM_ERROR);
454 	}
455 #endif
456 
457 	/* Everything is okay put the received data into user */
458 	/* application's resp_msg struct */
459 	resp_msg->msg_len = resp_msg_hdr->msg_len;
460 	resp_msg->msg_type = resp_msg_hdr->msg_type;
461 	resp_msg->sub_type = resp_msg_hdr->sub_type;
462 	resp_msg->msg_data = (uint8_t *)resp_msg_data;
463 
464 	return (PCPL_OK);
465 
466 }
467 
468 /*
469  * Function: Get channel property values.
470  * Arguments:
471  *	int prop - property id.
472  *	unsigned int *val - property value tobe copied.
473  * Returns:
474  *	0 - success
475  *	(-ve) - failure:
476  *		PCPL_ERR_GLVC - glvc ioctl failure.
477  */
478 
479 static int
480 pcp_get_prop(int prop, unsigned int *val)
481 {
482 	glvc_xport_opt_op_t	channel_op;
483 	int			ret;
484 
485 	channel_op.op_sel = GLVC_XPORT_OPT_GET;
486 	channel_op.opt_sel = prop;
487 	channel_op.opt_val = 0;
488 
489 	(void) alarm(glvc_timeout);
490 
491 	if ((ret = ioctl(chnl_fd, GLVC_XPORT_IOCTL_OPT_OP, &channel_op)) < 0) {
492 		(void) alarm(0);
493 		return (ret);
494 	}
495 	(void) alarm(0);
496 
497 	*val = channel_op.opt_val;
498 
499 	return (0);
500 }
501 
502 /*
503  * Function: wrapper for handling glvc calls (read/write/peek).
504  */
505 static int
506 pcp_io_op(void *buf, int byte_cnt, int io_op)
507 {
508 	int	rv;
509 	int	n;
510 	uint8_t	*datap;
511 	int	(*func_ptr)(uint8_t *, int);
512 	int	io_sz;
513 	int	try_cnt;
514 
515 
516 	if ((buf == NULL) || (byte_cnt < 0)) {
517 		return (PCPL_INVALID_ARGS);
518 	}
519 
520 	switch (io_op) {
521 		case PCPL_IO_OP_READ:
522 			func_ptr = pcp_read;
523 			break;
524 		case PCPL_IO_OP_WRITE:
525 			func_ptr = pcp_write;
526 			break;
527 		case PCPL_IO_OP_PEEK:
528 			func_ptr = pcp_peek;
529 			break;
530 		default:
531 			return (PCPL_INVALID_ARGS);
532 	}
533 
534 	/*
535 	 * loop until all I/O done, try limit exceded, or real failure
536 	 */
537 
538 	rv = 0;
539 	datap = buf;
540 	while (rv < byte_cnt) {
541 		io_sz = MIN((byte_cnt - rv), mtu_size);
542 		try_cnt = 0;
543 		while ((n = (*func_ptr)(datap, io_sz)) < 0) {
544 			try_cnt++;
545 			if (try_cnt > PCPL_MAX_TRY_CNT) {
546 				rv = n;
547 				goto done;
548 			}
549 			(void) sleep(PCPL_GLVC_SLEEP);
550 		} /* while trying the io operation */
551 
552 		if (n < 0) {
553 			rv = n;
554 			goto done;
555 		}
556 		rv += n;
557 		datap += n;
558 	} /* while still have more data */
559 
560 done:
561 	if (rv == byte_cnt)
562 		return (0);
563 	else
564 		return (PCPL_GLVC_ERROR);
565 }
566 
567 /*
568  * For peeking 'bytes_cnt' bytes in channel (glvc) buffers.
569  * If data is available, the data is copied into 'buf'.
570  */
571 static int
572 pcp_peek(uint8_t *buf, int bytes_cnt)
573 {
574 	int			ret;
575 	glvc_xport_msg_peek_t	peek_ctrl;
576 	int			n, m;
577 
578 	if (bytes_cnt < 0 || bytes_cnt > mtu_size) {
579 		return (PCPL_INVALID_ARGS);
580 	}
581 
582 	/*
583 	 * initialization of buffers used for peeking data in channel buffers.
584 	 */
585 	if (peek_area == NULL) {
586 		peek_area = (uint8_t *)umem_zalloc(PEEK_AREA_SIZE,
587 							UMEM_DEFAULT);
588 		if (peek_area == NULL) {
589 			return (PCPL_MALLOC_FAIL);
590 		}
591 	}
592 
593 	/*
594 	 * peek max MTU size bytes
595 	 */
596 	peek_ctrl.buf = (caddr_t)peek_area;
597 	peek_ctrl.buflen = mtu_size;
598 	peek_ctrl.flags = 0;
599 
600 	(void) alarm(glvc_timeout);
601 
602 	if ((ret = ioctl(chnl_fd, GLVC_XPORT_IOCTL_DATA_PEEK, &peek_ctrl))
603 		< 0) {
604 		(void) alarm(0);
605 		return (ret);
606 	}
607 	(void) alarm(0);
608 
609 	n = peek_ctrl.buflen;
610 
611 	if (n < 0)
612 		return (PCPL_GLVC_ERROR);
613 
614 	/*
615 	 * satisfy request as best as we can
616 	 */
617 	m = MIN(bytes_cnt, n);
618 	(void) memcpy(buf, peek_area, m);
619 
620 	return (m);
621 
622 }
623 
624 /*
625  * Function: write 'byte_cnt' bytes from 'buf' to channel.
626  */
627 static int
628 pcp_write(uint8_t *buf, int byte_cnt)
629 {
630 
631 	int	ret;
632 
633 	/* check for valid arguments */
634 	if (buf == NULL || byte_cnt < 0 || byte_cnt > mtu_size) {
635 		return (PCPL_INVALID_ARGS);
636 	}
637 
638 	(void) alarm(glvc_timeout);
639 
640 	if ((ret = write(chnl_fd, buf, byte_cnt)) < 0) {
641 		(void) alarm(0);
642 		return (ret);
643 	}
644 	(void) alarm(0);
645 
646 	return (ret);
647 }
648 
649 /*
650  * In current implementaion of glvc driver, streams reads are not supported.
651  * pcp_read mimics stream reads by first reading all the bytes present in the
652  * channel buffer into a local buffer and from then on read requests
653  * are serviced from local buffer. When read requests are not serviceble
654  * from local buffer, it repeates by first reading data from channel buffers.
655  *
656  * This call may need to be enhanced when glvc supports buffered (stream)
657  * reads - TBD
658  */
659 
660 static int
661 pcp_read(uint8_t *buf, int byte_cnt)
662 {
663 	int			ret;
664 	int			n, m, i;
665 
666 	if (byte_cnt < 0 || byte_cnt > mtu_size) {
667 		return (PCPL_INVALID_ARGS);
668 	}
669 
670 	/*
671 	 * initialization of local read buffer
672 	 * from which the stream read requests are serviced.
673 	 */
674 	if (read_area == NULL) {
675 		read_area = (uint8_t *)umem_zalloc(READ_AREA_SIZE,
676 							UMEM_DEFAULT);
677 		if (read_area == NULL) {
678 			return (PCPL_MALLOC_FAIL);
679 		}
680 		read_head = read_area;
681 		read_tail = read_area;
682 	}
683 
684 	/*
685 	 * if we already read this data then copy from local buffer it self
686 	 * without calling new read.
687 	 */
688 	if (byte_cnt <= (read_tail - read_head)) {
689 		(void) memcpy(buf, read_head, byte_cnt);
690 		read_head += byte_cnt;
691 		return (byte_cnt);
692 	}
693 
694 	/*
695 	 * if the request is not satisfied from the buffered data, then move the
696 	 * remaining data to front of the buffer and read new data.
697 	 */
698 	for (i = 0; i < (read_tail - read_head); ++i) {
699 		read_area[i] = read_head[i];
700 	}
701 	read_head = read_area;
702 	read_tail = read_head + i;
703 
704 	/*
705 	 * do a peek to see how much data is available and read complete data.
706 	 */
707 
708 	if ((m = pcp_peek(read_tail, mtu_size)) < 0) {
709 		return (m);
710 	}
711 
712 	(void) alarm(glvc_timeout);
713 	if ((ret = read(chnl_fd, read_tail, m)) < 0) {
714 		(void) alarm(0);
715 		return (ret);
716 	}
717 
718 	(void) alarm(0);
719 	read_tail += ret;
720 
721 	/*
722 	 * copy the requested bytes.
723 	 */
724 	n = MIN(byte_cnt, (read_tail - read_head));
725 	(void) memcpy(buf, read_head, n);
726 
727 	read_head += n;
728 
729 	return (n);
730 }
731 
732 /*
733  * This function is slight different from pcp_peek. The peek requests are first
734  * serviced from local read buffer, if data is available. If the peek request
735  * is not serviceble from local read buffer, then the data is peeked from
736  * channel buffer. This function is mainly used for proper protocol framing
737  * error handling.
738  */
739 static int
740 pcp_peek_read(uint8_t *buf, int byte_cnt)
741 {
742 	int	n, m, i;
743 
744 	if (byte_cnt < 0 || byte_cnt > mtu_size) {
745 		return (PCPL_INVALID_ARGS);
746 	}
747 
748 	/*
749 	 * initialization of peek_read buffer.
750 	 */
751 	if (peek_read_area == NULL) {
752 		peek_read_area = (uint8_t *)umem_zalloc(PEEK_READ_AREA_SIZE,
753 						UMEM_DEFAULT);
754 		if (peek_read_area == NULL) {
755 			return (PCPL_MALLOC_FAIL);
756 		}
757 		peek_read_head = peek_read_area;
758 		peek_read_tail = peek_read_area;
759 	}
760 
761 	/*
762 	 * if we already have the data in local read buffer then copy
763 	 * from local buffer it self w/out calling new peek
764 	 */
765 	if (byte_cnt <= (read_tail - read_head)) {
766 		(void) memcpy(buf, read_head, byte_cnt);
767 		return (byte_cnt);
768 	}
769 
770 	/*
771 	 * if the request is not satisfied from local read buffer, then first
772 	 * copy the remaining data in local read buffer to peek_read_area and
773 	 * then issue new peek.
774 	 */
775 	for (i = 0; i < (read_tail - read_head); ++i) {
776 		peek_read_area[i] = read_head[i];
777 	}
778 	peek_read_head = peek_read_area;
779 	peek_read_tail = peek_read_head + i;
780 
781 	/*
782 	 * do a peek to see how much data is available and read complete data.
783 	 */
784 
785 	if ((m = pcp_peek(peek_read_tail, mtu_size)) < 0) {
786 		return (m);
787 	}
788 
789 	peek_read_tail += m;
790 
791 	/*
792 	 * copy the requested bytes
793 	 */
794 	n = MIN(byte_cnt, (peek_read_tail - peek_read_head));
795 	(void) memcpy(buf, peek_read_head, n);
796 
797 	return (n);
798 }
799 
800 /*
801  * Send Request Message Header.
802  */
803 static int
804 pcp_send_req_msg_hdr(pcp_req_msg_hdr_t *req_hdr)
805 {
806 	pcp_req_msg_hdr_t	*hdrp;
807 	int			hdr_sz;
808 	int			ret;
809 
810 	hdr_sz = sizeof (pcp_req_msg_hdr_t);
811 	if ((hdrp = (pcp_req_msg_hdr_t *)umem_zalloc(hdr_sz,
812 						UMEM_DEFAULT)) == NULL) {
813 		return (PCPL_MALLOC_FAIL);
814 	}
815 
816 	hdrp->magic_num = htonl(req_hdr->magic_num);
817 	hdrp->proto_ver = req_hdr->proto_ver;
818 	hdrp->msg_type = req_hdr->msg_type;
819 	hdrp->sub_type = req_hdr->sub_type;
820 	hdrp->rsvd_pad = htons(req_hdr->rsvd_pad);
821 	hdrp->xid = htonl(req_hdr->xid);
822 	hdrp->timeout = htonl(req_hdr->timeout);
823 	hdrp->msg_len = htonl(req_hdr->msg_len);
824 	hdrp->msg_cksum = htons(req_hdr->msg_cksum);
825 	hdrp->hdr_cksum = htons(req_hdr->hdr_cksum);
826 
827 	if ((ret = pcp_io_op((char *)hdrp, hdr_sz, PCPL_IO_OP_WRITE)) != 0) {
828 		umem_free(hdrp, hdr_sz);
829 		return (ret);
830 	}
831 	umem_free(hdrp, hdr_sz);
832 	return (PCP_OK);
833 }
834 
835 /*
836  * Receive Response message header.
837  */
838 static int
839 pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr)
840 {
841 	uint32_t	magic_num;
842 	uint8_t		proto_ver;
843 	uint8_t		msg_type;
844 	uint8_t		sub_type;
845 	uint8_t		rsvd_pad;
846 	uint32_t	xid;
847 	uint32_t	timeout;
848 	uint32_t	msg_len;
849 	uint32_t	status;
850 	uint16_t	msg_cksum;
851 	uint16_t	hdr_cksum;
852 	int		ret;
853 
854 	if (resp_hdr == NULL) {
855 		return (PCPL_INVALID_ARGS);
856 	}
857 
858 	/*
859 	 * handle protocol framing errors.
860 	 * pcp_frame_error_handle() returns when proper frame arrived
861 	 * (magic seq) or if an error happens while reading data from
862 	 * channel.
863 	 */
864 	if ((ret = pcp_frame_error_handle()) != 0)
865 		return (PCPL_FRAME_ERROR);
866 
867 	/* read magic number first */
868 	if ((ret = pcp_io_op(&magic_num, sizeof (magic_num),
869 					PCPL_IO_OP_READ)) != 0) {
870 		return (ret);
871 	}
872 
873 	magic_num = ntohl(magic_num);
874 
875 	if (magic_num != PCP_MAGIC_NUM) {
876 		return (PCPL_FRAME_ERROR);
877 	}
878 
879 	/* read version field */
880 	if ((ret = pcp_io_op(&proto_ver, sizeof (proto_ver),
881 					PCPL_IO_OP_READ)) != 0) {
882 		return (ret);
883 	}
884 
885 	/* check protocol version */
886 	if (proto_ver != PCP_PROT_VER_1) {
887 		return (PCPL_PROT_ERROR);
888 	}
889 
890 	/* Read message type */
891 	if ((ret = pcp_io_op(&msg_type, sizeof (msg_type),
892 					PCPL_IO_OP_READ)) != 0) {
893 		return (ret);
894 	}
895 
896 	/* Read message sub type */
897 	if ((ret = pcp_io_op(&sub_type, sizeof (sub_type),
898 					PCPL_IO_OP_READ)) != 0) {
899 		return (ret);
900 	}
901 
902 	/* Read rcvd_pad bits */
903 	if ((ret = pcp_io_op(&rsvd_pad, sizeof (rsvd_pad),
904 					PCPL_IO_OP_READ)) != 0) {
905 		return (ret);
906 	}
907 
908 	/* receive transaction id */
909 	if ((ret = pcp_io_op(&xid, sizeof (xid),
910 			PCPL_IO_OP_READ)) != 0) {
911 		return (ret);
912 	}
913 
914 	xid = ntohl(xid);
915 
916 	/* receive timeout value */
917 	if ((ret = pcp_io_op(&timeout, sizeof (timeout),
918 				PCPL_IO_OP_READ)) != 0) {
919 		return (ret);
920 	}
921 
922 	timeout = ntohl(timeout);
923 
924 	/* receive message length */
925 	if ((ret = pcp_io_op(&msg_len, sizeof (msg_len),
926 				PCPL_IO_OP_READ)) != 0) {
927 		return (ret);
928 	}
929 
930 	msg_len = ntohl(msg_len);
931 
932 	/* receive status field */
933 	if ((ret = pcp_io_op(&status, sizeof (status),
934 				PCPL_IO_OP_READ)) != 0) {
935 		return (ret);
936 	}
937 
938 	status = ntohl(status);
939 
940 	/* receive message checksum */
941 	if ((ret = pcp_io_op(&msg_cksum, sizeof (msg_cksum),
942 					PCPL_IO_OP_READ)) != 0) {
943 		return (ret);
944 	}
945 
946 	msg_cksum = ntohs(msg_cksum);
947 
948 	/* receive header checksum */
949 	if ((ret = pcp_io_op(&hdr_cksum, sizeof (hdr_cksum),
950 					PCPL_IO_OP_READ)) != 0) {
951 		return (ret);
952 	}
953 
954 	hdr_cksum = ntohs(hdr_cksum);
955 
956 	/* copy to resp_hdr */
957 
958 	resp_hdr->magic_num = magic_num;
959 	resp_hdr->proto_ver = proto_ver;
960 	resp_hdr->msg_type = msg_type;
961 	resp_hdr->sub_type = sub_type;
962 	resp_hdr->rsvd_pad = rsvd_pad;
963 	resp_hdr->xid = xid;
964 	resp_hdr->timeout = timeout;
965 	resp_hdr->msg_len = msg_len;
966 	resp_hdr->status = status;
967 	resp_hdr->msg_cksum = msg_cksum;
968 	resp_hdr->hdr_cksum = hdr_cksum;
969 
970 	return (PCP_OK);
971 }
972 
973 /*
974  * Get next xid for including in request message.
975  * Every request and response message are matched
976  * for same xid.
977  */
978 static int pcp_get_xid(void)
979 {
980 	uint32_t ret;
981 
982 	ret = msg_xid;
983 	if (msg_xid == PCPL_MAX_XID)
984 		msg_xid = PCPL_MIN_XID;
985 	else
986 		++msg_xid;
987 	return (ret);
988 }
989 
990 /*
991  * This function handles channel framing errors. It waits until proper
992  * frame with starting sequence as magic numder (0xAFBCAFA0)
993  * is arrived. It removes unexpected data (before the magic number sequence)
994  * on the channel. It returns when proper magic number sequence is seen
995  * or when any failure happens while reading/peeking the channel.
996  */
997 static int
998 pcp_frame_error_handle(void)
999 {
1000 	uint8_t		magic_num_buf[4];
1001 	int		ispresent = 0;
1002 	uint32_t	net_magic_num; /* magic byte in network byte order */
1003 	uint32_t	host_magic_num = PCP_MAGIC_NUM;
1004 	uint8_t		buf[2];
1005 
1006 	net_magic_num =  htonl(host_magic_num);
1007 	(void) memcpy(magic_num_buf, (uint8_t *)&net_magic_num, 4);
1008 
1009 	while (!ispresent) {
1010 		/*
1011 		 * Check if next four bytes matches pcp magic number.
1012 		 * if mathing not found, discard 1 byte and continue checking.
1013 		 */
1014 		if (!check_magic_byte_presence(4, &magic_num_buf[0],
1015 							&ispresent)) {
1016 			if (!ispresent) {
1017 				/* remove 1 byte */
1018 				(void) pcp_io_op(buf, 1, PCPL_IO_OP_READ);
1019 			}
1020 		} else {
1021 			return (-1);
1022 		}
1023 	}
1024 
1025 	return (0);
1026 }
1027 
1028 /*
1029  * checks whether certain byte sequence is present in the data stream.
1030  */
1031 static int
1032 check_magic_byte_presence(int byte_cnt, uint8_t *byte_seq, int *ispresent)
1033 {
1034 	int		ret, i;
1035 	uint8_t		buf[4];
1036 
1037 	if ((ret = pcp_peek_read(buf, byte_cnt)) < 0) {
1038 		return (ret);
1039 	}
1040 
1041 	/* 'byte_cnt' bytes not present */
1042 	if (ret != byte_cnt) {
1043 		*ispresent = 0;
1044 		return (0);
1045 	}
1046 
1047 	for (i = 0; i < byte_cnt; ++i) {
1048 		if (buf[i] != byte_seq[i]) {
1049 			*ispresent = 0;
1050 			return (0);
1051 		}
1052 	}
1053 	*ispresent = 1;
1054 
1055 	return (0);
1056 }
1057 
1058 /*
1059  * 16-bit simple internet checksum
1060  */
1061 static uint16_t
1062 checksum(uint16_t *addr, int32_t count)
1063 {
1064 	/*
1065 	 * Compute Internet Checksum for "count" bytes
1066 	 * beginning at location "addr".
1067 	 */
1068 
1069 	register uint32_t	sum = 0;
1070 
1071 	while (count > 1)  {
1072 		/*  This is the inner loop */
1073 		sum += *(unsigned short *)addr++;
1074 		count -= 2;
1075 	}
1076 
1077 	/*  Add left-over byte, if any */
1078 	if (count > 0)
1079 		sum += * (unsigned char *)addr;
1080 
1081 	/* Fold 32-bit sum to 16 bits */
1082 	while (sum >> 16)
1083 		sum = (sum & 0xffff) + (sum >> 16);
1084 
1085 	sum = (~sum) & 0xffff;
1086 	if (sum == 0)
1087 		sum = 0xffff;
1088 
1089 	return (sum);
1090 }
1091 
1092 /*
1093  * cleanup the channel if any data is hanging in
1094  * channel buffers.
1095  */
1096 static int
1097 pcp_cleanup(void)
1098 {
1099 	int			ret;
1100 	glvc_xport_msg_peek_t	peek_ctrl;
1101 	int			n, done;
1102 	uint8_t			*buf = NULL;
1103 	int			retry = 0;
1104 
1105 
1106 	buf = (uint8_t *)umem_zalloc((mtu_size), UMEM_DEFAULT);
1107 	if (buf == NULL) {
1108 		return (PCPL_MALLOC_FAIL);
1109 	}
1110 
1111 	peek_ctrl.buf = (caddr_t)buf;
1112 	peek_ctrl.buflen = mtu_size;
1113 	peek_ctrl.flags = 0;
1114 
1115 	/*
1116 	 * set sig jmp location
1117 	 */
1118 	if (sigsetjmp(jmpbuf, 1)) {
1119 		umem_free(buf, mtu_size);
1120 		return (PCPL_GLVC_TIMEOUT);
1121 	}
1122 
1123 	done = 0;
1124 	while (!done) {
1125 
1126 		(void) alarm(PCP_CLEANUP_TIMEOUT);
1127 		if ((ret = ioctl(chnl_fd, GLVC_XPORT_IOCTL_DATA_PEEK,
1128 							&peek_ctrl)) < 0) {
1129 			(void) alarm(0);
1130 			done = 1;
1131 			continue;
1132 		}
1133 		(void) alarm(0);
1134 
1135 		n = peek_ctrl.buflen;
1136 
1137 		if (n <= 0 && retry > 2) {
1138 			done = 1;
1139 			continue;
1140 		} else if (n <= 0) {
1141 			++retry;
1142 			continue;
1143 		}
1144 
1145 		/* remove data from channel */
1146 		(void) alarm(PCP_CLEANUP_TIMEOUT);
1147 		if ((ret = read(chnl_fd, buf, n)) < 0) {
1148 			(void) alarm(0);
1149 			done = 1;
1150 			continue;
1151 		}
1152 		(void) alarm(0);
1153 	}
1154 
1155 	umem_free(buf, mtu_size);
1156 	return (ret);
1157 }
1158