#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <inttypes.h>
#include <umem.h>
#include <strings.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/glvc.h>
#include <sys/vldc.h>
#include <sys/ldc.h>
#include <netinet/in.h>
#include "libpcp.h"
#include "pcp_common.h"
#include "pcp_utils.h"
static int pcp_send_req_msg_hdr(pcp_req_msg_hdr_t *req_hdr);
static int pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr);
static int pcp_io_op(void *buf, int byte_cnt, int io_op);
static uint32_t pcp_get_xid(void);
static int pcp_get_prop(int channel_fd, int prop, unsigned int *val);
static int pcp_read(uint8_t *buf, int buf_len);
static int pcp_write(uint8_t *buf, int buf_len);
static int pcp_peek(uint8_t *buf, int buf_len);
static int pcp_peek_read(uint8_t *buf, int buf_len);
static int pcp_frame_error_handle(void);
static int check_magic_byte_presence(int byte_cnt, uint8_t *byte_val,
int *ispresent);
static uint16_t checksum(uint16_t *addr, int32_t count);
static int pcp_cleanup(int channel_fd);
static int vldc_read(int fd, uint8_t *bufp, int size);
static int vldc_write(int fd, uint8_t *bufp, int size);
static int pcp_update_read_area(int byte_cnt);
static int pcp_vldc_frame_error_handle(void);
static int chnl_fd = -1;
static uint32_t msg_xid = 0;
static unsigned int mtu_size = PCPL_DEF_MTU_SZ;
static uint32_t glvc_timeout = 0;
static volatile sig_atomic_t jumpok = 0;
static sigjmp_buf jmpbuf;
static sigset_t blkset;
#define READ_AREA_SIZE (2*mtu_size)
static uint8_t *read_head = NULL;
static uint8_t *read_tail = NULL;
static uint8_t *read_area = NULL;
#define PEEK_AREA_SIZE (mtu_size)
static uint8_t *peek_area = NULL;
#define PEEK_READ_AREA_SIZE (2*mtu_size)
static uint8_t *peek_read_head = NULL;
static uint8_t *peek_read_tail = NULL;
static uint8_t *peek_read_area = NULL;
static pcp_req_msg_hdr_t *req_msg_hdr = NULL;
static pcp_resp_msg_hdr_t *resp_msg_hdr = NULL;
static int req_msg_hdr_sz = 0;
static int resp_msg_hdr_sz = 0;
static struct sigaction glvc_act;
static struct sigaction old_act;
static pcp_xport_t xport_type = GLVC_NON_STREAM;
#define VLDC_MTU_SIZE (2048)
static void
glvc_timeout_handler(void)
{
if (jumpok == 0)
return;
siglongjmp(jmpbuf, 1);
}
int
pcp_init(char *channel_name)
{
sigset_t oldset;
int channel_fd;
char *dev_path;
vldc_opt_op_t op;
if (channel_name == NULL)
return (PCPL_INVALID_ARGS);
dev_path = platsvc_name_to_path(channel_name, &xport_type);
if (NULL == dev_path)
return (PCPL_INVALID_ARGS);
if ((channel_fd = open(dev_path, O_RDWR|O_EXCL)) < 0) {
free(dev_path);
return (PCPL_GLVC_ERROR);
}
free(dev_path);
switch (xport_type) {
case VLDC_STREAMING:
mtu_size = VLDC_MTU_SIZE;
op.op_sel = VLDC_OP_SET;
op.opt_sel = VLDC_OPT_MODE;
op.opt_val = LDC_MODE_RELIABLE;
if (ioctl(channel_fd, VLDC_IOCTL_OPT_OP, &op) != 0) {
(void) close(channel_fd);
return (PCPL_GLVC_ERROR);
}
break;
case GLVC_NON_STREAM:
default:
if (pcp_get_prop(channel_fd, GLVC_XPORT_OPT_MTU_SZ,
&mtu_size) != 0) {
(void) close(channel_fd);
return (PCPL_GLVC_ERROR);
}
break;
}
(void) sigprocmask(0, NULL, &oldset);
(void) sigemptyset(&blkset);
if (sigismember(&oldset, SIGALRM)) {
(void) sigaddset(&blkset, SIGALRM);
(void) sigprocmask(SIG_UNBLOCK, &blkset, NULL);
}
glvc_act.sa_handler = glvc_timeout_handler;
(void) sigemptyset(&glvc_act.sa_mask);
glvc_act.sa_flags = SA_NODEFER;
if (sigaction(SIGALRM, &glvc_act, &old_act) < 0) {
(void) close(channel_fd);
return (PCPL_ERROR);
}
return (channel_fd);
}
int
pcp_close(int channel_fd)
{
if (channel_fd >= 0) {
if (xport_type == GLVC_NON_STREAM)
(void) pcp_cleanup(channel_fd);
(void) close(channel_fd);
} else {
return (-1);
}
if (read_area != NULL) {
umem_free(read_area, READ_AREA_SIZE);
read_area = NULL;
}
if (peek_area != NULL) {
umem_free(peek_area, PEEK_AREA_SIZE);
peek_area = NULL;
}
if (peek_read_area != NULL) {
umem_free(peek_read_area, PEEK_READ_AREA_SIZE);
peek_read_area = NULL;
}
if (req_msg_hdr != NULL) {
umem_free(req_msg_hdr, req_msg_hdr_sz);
req_msg_hdr = NULL;
}
if (resp_msg_hdr != NULL) {
umem_free(resp_msg_hdr, resp_msg_hdr_sz);
resp_msg_hdr = NULL;
}
if (sigismember(&blkset, SIGALRM)) {
(void) sigprocmask(SIG_BLOCK, &blkset, NULL);
}
(void) sigaction(SIGALRM, &old_act, NULL);
return (PCPL_OK);
}
int
pcp_send_recv(int channel_fd, pcp_msg_t *req_msg, pcp_msg_t *resp_msg,
uint32_t timeout)
{
void *datap;
void *resp_msg_data = NULL;
uint32_t status;
uint16_t cksum = 0;
int ret;
int resp_hdr_ok;
#ifdef PCP_CKSUM_ENABLE
uint16_t bkup_resp_hdr_cksum;
#endif
if (channel_fd < 0) {
return (PCPL_ERROR);
}
chnl_fd = channel_fd;
if (req_msg == NULL) {
return (PCPL_INVALID_ARGS);
}
if (timeout > 0)
glvc_timeout = timeout;
else
glvc_timeout = 0;
if ((req_msg->msg_len != 0) && ((datap = req_msg->msg_data) == NULL))
return (PCPL_INVALID_ARGS);
if (req_msg_hdr == NULL) {
req_msg_hdr_sz = sizeof (pcp_req_msg_hdr_t);
req_msg_hdr = (pcp_req_msg_hdr_t *)umem_zalloc(req_msg_hdr_sz,
UMEM_DEFAULT);
if (req_msg_hdr == NULL)
return (PCPL_MALLOC_FAIL);
}
if (req_msg->msg_len != 0) {
cksum = checksum((uint16_t *)datap, req_msg->msg_len);
}
req_msg_hdr->magic_num = PCP_MAGIC_NUM;
req_msg_hdr->proto_ver = PCP_PROT_VER_1;
req_msg_hdr->msg_type = req_msg->msg_type;
req_msg_hdr->sub_type = req_msg->sub_type;
req_msg_hdr->rsvd_pad = 0;
req_msg_hdr->xid = pcp_get_xid();
req_msg_hdr->msg_len = req_msg->msg_len;
req_msg_hdr->timeout = timeout;
req_msg_hdr->msg_cksum = cksum;
req_msg_hdr->hdr_cksum = 0;
req_msg_hdr->hdr_cksum = checksum((uint16_t *)req_msg_hdr,
req_msg_hdr_sz);
if (sigsetjmp(jmpbuf, 1)) {
return (PCPL_GLVC_TIMEOUT);
}
jumpok = 1;
if ((ret = pcp_send_req_msg_hdr(req_msg_hdr))) {
return (ret);
}
if (req_msg->msg_len != 0) {
if ((ret = pcp_io_op(datap, req_msg->msg_len,
PCPL_IO_OP_WRITE))) {
return (ret);
}
}
if (timeout == (uint32_t)PCP_TO_NO_RESPONSE)
return (PCPL_OK);
if (resp_msg_hdr == NULL) {
resp_msg_hdr_sz = sizeof (pcp_resp_msg_hdr_t);
resp_msg_hdr = (pcp_resp_msg_hdr_t *)umem_alloc(resp_msg_hdr_sz,
UMEM_DEFAULT);
if (resp_msg_hdr == NULL)
return (PCPL_MALLOC_FAIL);
}
resp_hdr_ok = 0;
while (!resp_hdr_ok) {
if ((ret = pcp_recv_resp_msg_hdr(resp_msg_hdr))) {
return (ret);
}
#ifdef PCP_CKSUM_ENABLE
bkup_resp_hdr_cksum = resp_msg_hdr->hdr_cksum;
resp_msg_hdr->hdr_cksum = 0;
cksum = checksum((uint16_t *)resp_msg_hdr, resp_msg_hdr_sz);
if (cksum != bkup_resp_hdr_cksum) {
return (PCPL_CKSUM_ERROR);
}
#endif
if (resp_msg_hdr->xid != req_msg_hdr->xid) {
continue;
}
resp_hdr_ok = 1;
}
status = resp_msg_hdr->status;
if (status != PCP_OK) {
return (PCPL_XPORT_ERROR);
}
if (resp_msg_hdr->msg_len != 0) {
if ((resp_msg_data = (uint8_t *)malloc(resp_msg_hdr->msg_len))
== NULL)
return (PCPL_MALLOC_FAIL);
bzero(resp_msg_data, resp_msg_hdr->msg_len);
if ((ret = pcp_io_op(resp_msg_data, resp_msg_hdr->msg_len,
PCPL_IO_OP_READ))) {
free(resp_msg_data);
return (ret);
}
#ifdef PCP_CKSUM_ENABLE
cksum = checksum((uint16_t *)resp_msg_data,
resp_msg_hdr->msg_len);
if (cksum != resp_msg_hdr->msg_cksum) {
free(resp_msg_data);
return (PCPL_CKSUM_ERROR);
}
#endif
}
resp_msg->msg_len = resp_msg_hdr->msg_len;
resp_msg->msg_type = resp_msg_hdr->msg_type;
resp_msg->sub_type = resp_msg_hdr->sub_type;
resp_msg->msg_data = (uint8_t *)resp_msg_data;
return (PCPL_OK);
}
static int
pcp_get_prop(int channel_fd, int prop, unsigned int *val)
{
glvc_xport_opt_op_t channel_op;
int ret;
channel_op.op_sel = GLVC_XPORT_OPT_GET;
channel_op.opt_sel = prop;
channel_op.opt_val = 0;
(void) alarm(glvc_timeout);
if ((ret = ioctl(channel_fd, GLVC_XPORT_IOCTL_OPT_OP,
&channel_op)) < 0) {
(void) alarm(0);
return (ret);
}
(void) alarm(0);
*val = channel_op.opt_val;
return (0);
}
static int
pcp_io_op(void *buf, int byte_cnt, int io_op)
{
int rv;
int n;
uint8_t *datap;
int (*func_ptr)(uint8_t *, int);
int io_sz;
int try_cnt;
if ((buf == NULL) || (byte_cnt < 0)) {
return (PCPL_INVALID_ARGS);
}
switch (io_op) {
case PCPL_IO_OP_READ:
func_ptr = pcp_read;
break;
case PCPL_IO_OP_WRITE:
func_ptr = pcp_write;
break;
case PCPL_IO_OP_PEEK:
func_ptr = pcp_peek;
break;
default:
return (PCPL_INVALID_ARGS);
}
rv = 0;
datap = buf;
while (rv < byte_cnt) {
io_sz = MIN((byte_cnt - rv), mtu_size);
try_cnt = 0;
while ((n = (*func_ptr)(datap, io_sz)) < 0) {
try_cnt++;
if (try_cnt > PCPL_MAX_TRY_CNT) {
rv = n;
goto done;
}
(void) sleep(PCPL_GLVC_SLEEP);
}
if (n < 0) {
rv = n;
goto done;
}
rv += n;
datap += n;
}
done:
if (rv == byte_cnt)
return (0);
else
return (PCPL_GLVC_ERROR);
}
static int
pcp_peek(uint8_t *buf, int bytes_cnt)
{
int ret;
glvc_xport_msg_peek_t peek_ctrl;
int n, m;
if (bytes_cnt < 0 || bytes_cnt > mtu_size) {
return (PCPL_INVALID_ARGS);
}
if (peek_area == NULL) {
peek_area = (uint8_t *)umem_zalloc(PEEK_AREA_SIZE,
UMEM_DEFAULT);
if (peek_area == NULL) {
return (PCPL_MALLOC_FAIL);
}
}
peek_ctrl.buf = (caddr_t)peek_area;
peek_ctrl.buflen = mtu_size;
peek_ctrl.flags = 0;
(void) alarm(glvc_timeout);
if ((ret = ioctl(chnl_fd, GLVC_XPORT_IOCTL_DATA_PEEK, &peek_ctrl))
< 0) {
(void) alarm(0);
return (ret);
}
(void) alarm(0);
n = peek_ctrl.buflen;
if (n < 0)
return (PCPL_GLVC_ERROR);
m = MIN(bytes_cnt, n);
(void) memcpy(buf, peek_area, m);
return (m);
}
static int
pcp_write(uint8_t *buf, int byte_cnt)
{
int ret;
if (buf == NULL || byte_cnt < 0 || byte_cnt > mtu_size) {
return (PCPL_INVALID_ARGS);
}
if (xport_type == GLVC_NON_STREAM) {
(void) alarm(glvc_timeout);
if ((ret = write(chnl_fd, buf, byte_cnt)) < 0) {
(void) alarm(0);
return (ret);
}
(void) alarm(0);
} else {
if ((ret = vldc_write(chnl_fd, buf, byte_cnt)) <= 0) {
return (ret);
}
}
return (ret);
}
static int
pcp_read(uint8_t *buf, int byte_cnt)
{
int ret;
int n, m, i;
if (byte_cnt < 0 || byte_cnt > mtu_size) {
return (PCPL_INVALID_ARGS);
}
if (read_area == NULL) {
read_area = (uint8_t *)umem_zalloc(READ_AREA_SIZE,
UMEM_DEFAULT);
if (read_area == NULL) {
return (PCPL_MALLOC_FAIL);
}
read_head = read_area;
read_tail = read_area;
}
if (byte_cnt <= (read_tail - read_head)) {
(void) memcpy(buf, read_head, byte_cnt);
read_head += byte_cnt;
return (byte_cnt);
}
for (i = 0; i < (read_tail - read_head); ++i) {
read_area[i] = read_head[i];
}
read_head = read_area;
read_tail = read_head + i;
if (xport_type == GLVC_NON_STREAM) {
if ((m = pcp_peek(read_tail, mtu_size)) < 0) {
return (m);
}
(void) alarm(glvc_timeout);
if ((ret = read(chnl_fd, read_tail, m)) < 0) {
(void) alarm(0);
return (ret);
}
(void) alarm(0);
} else {
m = byte_cnt - (read_tail - read_head);
if ((ret = vldc_read(chnl_fd,
read_tail, m)) <= 0) {
return (ret);
}
}
read_tail += ret;
n = MIN(byte_cnt, (read_tail - read_head));
(void) memcpy(buf, read_head, n);
read_head += n;
return (n);
}
static int
pcp_update_read_area(int byte_cnt)
{
int ret;
int n, i;
if (byte_cnt < 0 || byte_cnt > mtu_size) {
return (PCPL_INVALID_ARGS);
}
if (read_area == NULL) {
read_area = (uint8_t *)umem_zalloc(READ_AREA_SIZE,
UMEM_DEFAULT);
if (read_area == NULL) {
return (PCPL_MALLOC_FAIL);
}
read_head = read_area;
read_tail = read_area;
}
if (byte_cnt <= (read_tail - read_head)) {
return (byte_cnt);
}
for (i = 0; i < (read_tail - read_head); ++i) {
read_area[i] = read_head[i];
}
read_head = read_area;
read_tail = read_head + i;
n = byte_cnt - (read_tail - read_head);
if ((ret = vldc_read(chnl_fd,
read_tail, n)) <= 0) {
return (ret);
}
read_tail += ret;
n = MIN(byte_cnt, (read_tail - read_head));
return (n);
}
static int
pcp_peek_read(uint8_t *buf, int byte_cnt)
{
int n, m, i;
if (byte_cnt < 0 || byte_cnt > mtu_size) {
return (PCPL_INVALID_ARGS);
}
if (peek_read_area == NULL) {
peek_read_area = (uint8_t *)umem_zalloc(PEEK_READ_AREA_SIZE,
UMEM_DEFAULT);
if (peek_read_area == NULL) {
return (PCPL_MALLOC_FAIL);
}
peek_read_head = peek_read_area;
peek_read_tail = peek_read_area;
}
if (byte_cnt <= (read_tail - read_head)) {
(void) memcpy(buf, read_head, byte_cnt);
return (byte_cnt);
}
for (i = 0; i < (read_tail - read_head); ++i) {
peek_read_area[i] = read_head[i];
}
peek_read_head = peek_read_area;
peek_read_tail = peek_read_head + i;
if ((m = pcp_peek(peek_read_tail, mtu_size)) < 0) {
return (m);
}
peek_read_tail += m;
n = MIN(byte_cnt, (peek_read_tail - peek_read_head));
(void) memcpy(buf, peek_read_head, n);
return (n);
}
static int
pcp_send_req_msg_hdr(pcp_req_msg_hdr_t *req_hdr)
{
pcp_req_msg_hdr_t *hdrp;
int hdr_sz;
int ret;
hdr_sz = sizeof (pcp_req_msg_hdr_t);
if ((hdrp = (pcp_req_msg_hdr_t *)umem_zalloc(hdr_sz,
UMEM_DEFAULT)) == NULL) {
return (PCPL_MALLOC_FAIL);
}
hdrp->magic_num = htonl(req_hdr->magic_num);
hdrp->proto_ver = req_hdr->proto_ver;
hdrp->msg_type = req_hdr->msg_type;
hdrp->sub_type = req_hdr->sub_type;
hdrp->rsvd_pad = htons(req_hdr->rsvd_pad);
hdrp->xid = htonl(req_hdr->xid);
hdrp->timeout = htonl(req_hdr->timeout);
hdrp->msg_len = htonl(req_hdr->msg_len);
hdrp->msg_cksum = htons(req_hdr->msg_cksum);
hdrp->hdr_cksum = htons(req_hdr->hdr_cksum);
if ((ret = pcp_io_op((char *)hdrp, hdr_sz, PCPL_IO_OP_WRITE)) != 0) {
umem_free(hdrp, hdr_sz);
return (ret);
}
umem_free(hdrp, hdr_sz);
return (PCP_OK);
}
static int
pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr)
{
uint32_t magic_num;
uint8_t proto_ver;
uint8_t msg_type;
uint8_t sub_type;
uint8_t rsvd_pad;
uint32_t xid;
uint32_t timeout;
uint32_t msg_len;
uint32_t status;
uint16_t msg_cksum;
uint16_t hdr_cksum;
int ret;
if (resp_hdr == NULL) {
return (PCPL_INVALID_ARGS);
}
if (xport_type == GLVC_NON_STREAM)
ret = pcp_frame_error_handle();
else
ret = pcp_vldc_frame_error_handle();
if (ret != 0)
return (PCPL_FRAME_ERROR);
if ((ret = pcp_io_op(&magic_num, sizeof (magic_num),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
magic_num = ntohl(magic_num);
if (magic_num != PCP_MAGIC_NUM) {
return (PCPL_FRAME_ERROR);
}
if ((ret = pcp_io_op(&proto_ver, sizeof (proto_ver),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
if (proto_ver != PCP_PROT_VER_1) {
return (PCPL_PROT_ERROR);
}
if ((ret = pcp_io_op(&msg_type, sizeof (msg_type),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
if ((ret = pcp_io_op(&sub_type, sizeof (sub_type),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
if ((ret = pcp_io_op(&rsvd_pad, sizeof (rsvd_pad),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
if ((ret = pcp_io_op(&xid, sizeof (xid),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
xid = ntohl(xid);
if ((ret = pcp_io_op(&timeout, sizeof (timeout),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
timeout = ntohl(timeout);
if ((ret = pcp_io_op(&msg_len, sizeof (msg_len),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
msg_len = ntohl(msg_len);
if ((ret = pcp_io_op(&status, sizeof (status),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
status = ntohl(status);
if ((ret = pcp_io_op(&msg_cksum, sizeof (msg_cksum),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
msg_cksum = ntohs(msg_cksum);
if ((ret = pcp_io_op(&hdr_cksum, sizeof (hdr_cksum),
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
hdr_cksum = ntohs(hdr_cksum);
resp_hdr->magic_num = magic_num;
resp_hdr->proto_ver = proto_ver;
resp_hdr->msg_type = msg_type;
resp_hdr->sub_type = sub_type;
resp_hdr->rsvd_pad = rsvd_pad;
resp_hdr->xid = xid;
resp_hdr->timeout = timeout;
resp_hdr->msg_len = msg_len;
resp_hdr->status = status;
resp_hdr->msg_cksum = msg_cksum;
resp_hdr->hdr_cksum = hdr_cksum;
return (PCP_OK);
}
static uint32_t
pcp_get_xid(void)
{
uint32_t ret;
struct timeval tv;
static boolean_t xid_initialized = B_FALSE;
if (xid_initialized == B_FALSE) {
xid_initialized = B_TRUE;
(void) gettimeofday(&tv, NULL);
msg_xid = (uint32_t)((tv.tv_sec << 20) |
(tv.tv_usec >> 10));
}
ret = msg_xid++;
if (ret == 0)
ret = msg_xid++;
return (ret);
}
static int
pcp_frame_error_handle(void)
{
uint8_t magic_num_buf[4];
int ispresent = 0;
uint32_t net_magic_num;
uint32_t host_magic_num = PCP_MAGIC_NUM;
uint8_t buf[2];
net_magic_num = htonl(host_magic_num);
(void) memcpy(magic_num_buf, (uint8_t *)&net_magic_num, 4);
while (!ispresent) {
if (!check_magic_byte_presence(4, &magic_num_buf[0],
&ispresent)) {
if (!ispresent) {
(void) pcp_io_op(buf, 1, PCPL_IO_OP_READ);
}
} else {
return (-1);
}
}
return (0);
}
static int
pcp_vldc_frame_error_handle(void)
{
uint8_t magic_num_buf[4];
uint32_t net_magic_num;
uint32_t host_magic_num = PCP_MAGIC_NUM;
int found_magic = 0;
net_magic_num = htonl(host_magic_num);
(void) memcpy(magic_num_buf, (uint8_t *)&net_magic_num, 4);
while (!found_magic) {
while ((read_tail - read_head) < sizeof (host_magic_num)) {
if (pcp_update_read_area(sizeof (host_magic_num)) < 0)
return (-1);
}
if (memcmp(read_head, magic_num_buf,
sizeof (host_magic_num))) {
read_head += 1;
} else {
found_magic = 1;
}
}
return (0);
}
static int
check_magic_byte_presence(int byte_cnt, uint8_t *byte_seq, int *ispresent)
{
int ret, i;
uint8_t buf[4];
if ((ret = pcp_peek_read(buf, byte_cnt)) < 0) {
return (ret);
}
if (ret != byte_cnt) {
*ispresent = 0;
return (0);
}
for (i = 0; i < byte_cnt; ++i) {
if (buf[i] != byte_seq[i]) {
*ispresent = 0;
return (0);
}
}
*ispresent = 1;
return (0);
}
static uint16_t
checksum(uint16_t *addr, int32_t count)
{
register uint32_t sum = 0;
while (count > 1) {
sum += *(unsigned short *)addr++;
count -= 2;
}
if (count > 0)
sum += * (unsigned char *)addr;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
sum = (~sum) & 0xffff;
if (sum == 0)
sum = 0xffff;
return (sum);
}
static int
pcp_cleanup(int channel_fd)
{
int ret;
glvc_xport_msg_peek_t peek_ctrl;
int n, done;
uint8_t *buf = NULL;
int retry = 0;
buf = (uint8_t *)umem_zalloc((mtu_size), UMEM_DEFAULT);
if (buf == NULL) {
return (PCPL_MALLOC_FAIL);
}
peek_ctrl.buf = (caddr_t)buf;
peek_ctrl.buflen = mtu_size;
peek_ctrl.flags = 0;
if (sigsetjmp(jmpbuf, 1)) {
umem_free(buf, mtu_size);
return (PCPL_GLVC_TIMEOUT);
}
done = 0;
while (!done) {
(void) alarm(PCP_CLEANUP_TIMEOUT);
if ((ret = ioctl(channel_fd, GLVC_XPORT_IOCTL_DATA_PEEK,
&peek_ctrl)) < 0) {
(void) alarm(0);
done = 1;
continue;
}
(void) alarm(0);
n = peek_ctrl.buflen;
if (n <= 0 && retry > 2) {
done = 1;
continue;
} else if (n <= 0) {
++retry;
continue;
}
(void) alarm(PCP_CLEANUP_TIMEOUT);
if ((ret = read(channel_fd, buf, n)) < 0) {
(void) alarm(0);
done = 1;
continue;
}
(void) alarm(0);
}
umem_free(buf, mtu_size);
return (ret);
}
static int
vldc_write(int fd, uint8_t *bufp, int size)
{
int res;
int left = size;
pollfd_t pollfd;
pollfd.events = POLLOUT;
pollfd.revents = 0;
pollfd.fd = fd;
if (poll(&pollfd, 1, glvc_timeout * MILLISEC) <= 0) {
return (-1);
}
do {
if ((res = write(fd, bufp, left)) <= 0) {
if (errno != EWOULDBLOCK) {
return (res);
}
} else {
bufp += res;
left -= res;
}
} while (left > 0);
return (size - left);
}
static int
vldc_read(int fd, uint8_t *bufp, int size)
{
int res;
int left = size;
struct pollfd fds[1];
fds[0].events = POLLIN | POLLPRI;
fds[0].revents = 0;
fds[0].fd = fd;
if (poll(fds, 1, glvc_timeout * MILLISEC) <= 0) {
return (-1);
}
while (left > 0) {
res = read(fd, bufp, left);
if ((res == 0) || ((res < 0) &&
(errno == EAGAIN))) {
if ((poll(fds, 1, glvc_timeout * MILLISEC)) < 0)
return (-1);
continue;
} else
if (res < 0) {
return (-1);
} else {
bufp += res;
left -= res;
}
}
return (size - left);
}