/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/inttypes.h>
/* common defines */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
#ifndef ABS
#define ABS(x) ((x) < (0) ? (-(x)) : (x))
#endif
#define PCP_CKSUM_ENABLE
/* Error codes for 'status' field in response message header */
/*
* magic number for Platform Channel Protocol (PCP)
* ~(rot13("PCP_") = 0xAFBCAFA0
* rot13 is a simple Caesar-cypher encryption that replaces each English letter
* with the one 13 places forward or back along the alphabet.
*/
/* Platform channel protocol versions. */
/* defines for 'timeout' */
/* it waits until glvc driver */
/* call returns; curently glvc */
/* calls are blocking calls. */
/* Message Types */
/* alarm_action */
/* alarm_id */
#define PCP_ALARM_CRITICAL 0
/* alarm_state */
/* Status Types */
/* tsalarm service channel */
/* Driver state flags */
/*
* Platform Channel Request Message Header.
*/
typedef struct tsal_pcp_req_msg_hdr {
/* backward compatibility */
/*
* Platform Channel Response Message Header.
*/
typedef struct tsal_pcp_resp_msg_hdr {
/* backward compatibility */
/*
* PCP user apps message format
*/
typedef struct tsal_pcp_msg {
void *msg_data;
/*
*/
typedef struct tsal_pcp_alarm_req {
/*
*/
typedef struct tsal_pcp_alarm_resp {
/*
* tsalarm driver soft structure
*/
typedef struct tsalarm_softc {
int flags;
/*
* Forward declarations.
*/
/*
* Driver entry points
*/
/* dev_ops and cb_ops entry point function declarations */
tsalarm_open, /* open */
tsalarm_close, /* close */
nodev, /* strategy() */
nodev, /* print() */
nodev, /* dump() */
nodev, /* read() */
nodev, /* write() */
tsalarm_ioctl, /* ioctl() */
nodev, /* devmap() */
nodev, /* mmap() */
ddi_segmap, /* segmap() */
nochpoll, /* poll() */
ddi_prop_op, /* prop_op() */
NULL, /* cb_str */
};
0, /* ref count */
tsalarm_getinfo, /* getinfo() */
nulldev, /* identify() */
nulldev, /* probe() */
tsalarm_attach, /* attach() */
tsalarm_detach, /* detach */
nodev, /* reset */
&tsalarm_cb_ops, /* pointer to cb_ops structure */
nulldev, /* power() */
ddi_quiesce_not_needed, /* quiesce() */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
static void *statep;
&mod_driverops, /* Type of module. This is a driver */
"tsalarm control driver", /* Name of the module */
&tsalarm_ops /* pointer to the dev_ops structure */
};
&modldrv,
};
int
_init(void)
{
int e;
if (e = ddi_soft_state_init(&statep,
sizeof (struct tsalarm_softc), 1)) {
return (e);
}
if ((e = mod_install(&modlinkage)) != 0) {
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0) {
return (e);
}
return (DDI_SUCCESS);
}
int
{
}
/* ARGSUSED */
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
} else {
}
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default:
}
return (retval);
}
static int
{
int inst;
switch (cmd) {
case DDI_ATTACH:
/*
* Allocate a soft state structure for this instance.
*/
goto attach_failed;
}
/*
* Create minor node. The minor device number, inst, has no
* meaning. The model number above, which will be added to
* the device's softc, is used to direct peculiar behavior.
*/
goto attach_failed;
}
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* Free soft state, if allocated. remove minor node if added earlier */
if (softc) {
}
return (DDI_FAILURE);
}
static int
{
int inst;
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
/*
* Free the soft state and remove minor node added earlier.
*/
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
{
int rval;
return (EIO);
}
if (rv != 0) {
goto FAIL;
}
if (rv != 0) {
goto FAIL;
}
/* Get the MTU of the target channel */
channel_op.opt_val = 0;
goto FAIL;
}
sizeof (tsal_pcp_alarm_req_t),
KM_NOSLEEP)) == NULL) {
goto FAIL;
}
sizeof (tsal_pcp_alarm_resp_t),
KM_NOSLEEP)) == NULL) {
goto FAIL;
}
sizeof (tsal_pcp_req_msg_hdr_t),
KM_NOSLEEP)) == NULL) {
goto FAIL;
}
sizeof (tsal_pcp_resp_msg_hdr_t),
KM_NOSLEEP)) == NULL) {
goto FAIL;
}
KM_NOSLEEP)) == NULL) {
goto FAIL;
}
KM_NOSLEEP)) == NULL) {
goto FAIL;
}
rv = 0;
FAIL:
if (rv != 0) {
sizeof (tsal_pcp_alarm_req_t));
sizeof (tsal_pcp_alarm_resp_t));
sizeof (tsal_pcp_req_msg_hdr_t));
sizeof (tsal_pcp_resp_msg_hdr_t));
}
return (rv);
}
/* ARGSUSED */
static int
{
return (EIO);
}
if (rv != 0) {
}
/*
* free global buffers
*/
}
sizeof (tsal_pcp_alarm_req_t));
}
sizeof (tsal_pcp_alarm_resp_t));
}
sizeof (tsal_pcp_req_msg_hdr_t));
}
sizeof (tsal_pcp_resp_msg_hdr_t));
}
}
}
return (rv);
}
/* ARGSUSED */
static int
{
int retval = 0;
return (ENXIO);
switch (cmd) {
case LOMIOCALSTATE:
case LOMIOCALSTATE_OLD:
{
sizeof (ts_aldata_t), mode) != 0) {
goto end;
}
if ((alarm_type < ALARM_CRITICAL) ||
(alarm_type > ALARM_USER)) {
goto end;
}
softc);
if (retval != 0)
goto end;
goto end;
}
sizeof (ts_aldata_t), mode) != 0) {
goto end;
}
}
break;
case LOMIOCALCTL:
case LOMIOCALCTL_OLD:
{
sizeof (ts_aldata_t), mode) != 0) {
goto end;
}
if ((alarm_type < ALARM_CRITICAL) ||
(alarm_type > ALARM_USER)) {
goto end;
}
if ((alarm_state < ALARM_OFF) ||
(alarm_state > ALARM_ON)) {
goto end;
}
}
break;
default:
break;
}
end:
return (retval);
}
static int
{
/*
* setup the request data to attach to the libpcp msg
*/
goto alarm_return;
}
/*
* send the request, receive the response
*/
PCP_COMM_TIMEOUT) < 0) {
/* we either timed out or erred; either way try again */
PCP_COMM_TIMEOUT) < 0) {
goto alarm_return;
}
}
/*
* validate that this data was meant for us
*/
goto alarm_return;
}
/*
* verify that the Alarm action has taken place
*/
goto alarm_return;
}
return (status);
}
static int
{
/*
* setup the request data to attach to the libpcp msg
*/
sizeof (tsal_pcp_alarm_req_t),
KM_NOSLEEP)) == NULL)
goto alarm_return;
}
/*
* send the request, receive the response
*/
PCP_COMM_TIMEOUT) < 0) {
/* we either timed out or erred; either way try again */
PCP_COMM_TIMEOUT) < 0) {
goto alarm_return;
}
}
/*
* validate that this data was meant for us
*/
goto alarm_return;
}
/*
* verify that the Alarm action has taken place
*/
goto alarm_return;
}
/*
* ensure the Alarm action taken is the one requested
*/
goto alarm_return;
goto alarm_return;
goto alarm_return;
}
return (status);
}
/*
* Function: Send and Receive messages on platform channel.
* Arguments:
* int channel_fd - channel file descriptor.
* tsal_pcp_msg_t *req_msg - Request Message to send to other end of channel.
* tsal_pcp_msg_t *resp_msg - Response Message to be received.
* uint32_t timeout - timeout field when waiting for data from channel.
* Returns:
* 0 - success (TSAL_PCP_OK).
* (-1) - failure (TSAL_PCP_ERROR).
*/
static int
{
void *datap;
int ret;
int resp_hdr_ok;
#ifdef PCP_CKSUM_ENABLE
#endif
return (TSAL_PCP_ERROR);
}
return (TSAL_PCP_ERROR);
if (req_msg_hdr == NULL)
return (TSAL_PCP_ERROR);
/* calculate request msg_cksum */
}
/*
* Fill in the message header for the request packet
*/
req_msg_hdr->rsvd_pad = 0;
req_msg_hdr->hdr_cksum = 0;
/* fill request header checksum */
sizeof (tsal_pcp_req_msg_hdr_t));
/*
* send request message header
*/
return (ret);
}
/*
* send request message
*/
PCP_IO_OP_WRITE))) {
return (ret);
}
}
return (TSAL_PCP_OK);
if (resp_msg_hdr == NULL) {
return (TSAL_PCP_ERROR);
}
resp_hdr_ok = 0;
while (!resp_hdr_ok) {
/*
* Receive response message header
* Note: frame error handling is done in
* 'tsal_pcp_recv_resp_msg_hdr()'.
*/
return (ret);
}
/*
* Check header checksum if it matches with the received hdr
* checksum.
*/
#ifdef PCP_CKSUM_ENABLE
resp_msg_hdr->hdr_cksum = 0;
sizeof (tsal_pcp_resp_msg_hdr_t));
if (cksum != bkup_resp_hdr_cksum) {
return (TSAL_PCP_ERROR);
}
#endif
/*
* Check for matching request and response messages
*/
continue; /* continue reading response header */
}
resp_hdr_ok = 1;
}
/*
* check status field for any channel protocol errrors
* This field signifies something happend during request
* message trasmission. This field is set by the receiver.
*/
if (status != TSAL_PCP_OK) {
return (TSAL_PCP_ERROR);
}
if (resp_msg_hdr->msg_len != 0) {
return (TSAL_PCP_ERROR);
/*
* Receive response message.
*/
PCP_IO_OP_READ))) {
return (ret);
}
#ifdef PCP_CKSUM_ENABLE
/* verify response message data checksum */
return (TSAL_PCP_ERROR);
}
#endif
}
/* Everything is okay put the received data into user */
/* resp_msg struct */
return (TSAL_PCP_OK);
}
/*
*/
static int
{
int rv;
int n;
int io_sz;
int try_cnt;
return (TSAL_PCP_ERROR);
}
switch (io_op) {
case PCP_IO_OP_READ:
break;
case PCP_IO_OP_WRITE:
break;
case PCP_IO_OP_PEEK:
break;
default:
return (TSAL_PCP_ERROR);
}
/*
* loop until all I/O done, try limit exceded, or real failure
*/
rv = 0;
try_cnt = 0;
try_cnt++;
if (try_cnt > PCP_MAX_TRY_CNT) {
rv = n;
goto done;
}
/* waiting 5 secs. Do we need 5 Secs? */
} /* while trying the io operation */
if (n < 0) {
rv = n;
goto done;
}
rv += n;
datap += n;
} /* while still have more data */
done:
return (0);
else
return (TSAL_PCP_ERROR);
}
/*
* For peeking 'bytes_cnt' bytes in channel (glvc) buffers.
* If data is available, the data is copied into 'buf'.
*/
static int
{
int n, m;
return (TSAL_PCP_ERROR);
}
/*
* initialization of buffers used for peeking data in channel buffers.
*/
return (TSAL_PCP_ERROR);
}
/*
* peek max MTU size bytes
*/
return (ret);
}
if (n < 0)
return (TSAL_PCP_ERROR);
/*
* satisfy request as best as we can
*/
return (m);
}
/*
* Function: write 'byte_cnt' bytes from 'buf' to channel.
*/
static int
{
int ret;
/* check for valid arguments */
return (TSAL_PCP_ERROR);
}
uio.uio_loffset = 0;
return (ret);
}
}
/*
* In current implementaion of glvc driver, streams reads are not supported.
* tsal_pcp_read mimics stream reads by first reading all the bytes present in
* channel buffer into a local buffer and from then on read requests
* are serviced from local buffer. When read requests are not serviceble
* from local buffer, it repeates by first reading data from channel buffers.
*/
static int
{
int ret;
int n, m, i;
int read_area_size = 0;
return (TSAL_PCP_ERROR);
}
/*
* initialization of local read buffer
* from which the stream read requests are serviced.
*/
return (TSAL_PCP_ERROR);
}
}
/*
* if we already read this data then copy from local buffer it self
* without calling new read.
*/
return (byte_cnt);
}
/*
* if the request is not satisfied from the buffered data, then move
* remaining data to front of the buffer and read new data.
*/
}
/*
* do a peek to see how much data is available and read complete data.
*/
return (m);
}
uio.uio_loffset = 0;
return (ret);
}
/*
* copy the requested bytes.
*/
return (n);
}
/*
* This function is slight different from tsal_pcp_peek. The peek requests are
* serviced from local read buffer, if data is available. If the peek request
* is not serviceble from local read buffer, then the data is peeked from
* channel buffer. This function is mainly used for proper protocol framing
* error handling.
*/
static int
{
int n, m, i;
return (TSAL_PCP_ERROR);
}
/*
* if we already have the data in local read buffer then copy
*/
return (byte_cnt);
}
return (TSAL_PCP_ERROR);
}
/*
* if the request is not satisfied from local read buffer, then first
* copy the remaining data in local read buffer to peek_read_area and
* then issue new peek.
*/
}
peek_read_tail = peek_read_head + i;
/*
* do a peek to see how much data is available and read complete data.
*/
return (m);
}
peek_read_tail += m;
/*
* copy the requested bytes
*/
return (n);
}
/*
* Send Request Message Header.
*/
static int
{
int hdr_sz;
int ret;
hdr_sz = sizeof (tsal_pcp_req_msg_hdr_t);
KM_NOSLEEP)) == NULL) {
return (TSAL_PCP_ERROR);
}
PCP_IO_OP_WRITE)) != 0) {
return (ret);
}
return (TSAL_PCP_OK);
}
/*
* Receive Response message header.
*/
static int
{
int ret;
return (TSAL_PCP_ERROR);
}
/*
* handle protocol framing errors.
* tsal_pcp_frame_error_handle() returns when proper frame arrived
* (magic seq) or if an error happens while reading data from
* channel.
*/
return (TSAL_PCP_ERROR);
}
/* read magic number first */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
if (magic_num != PCP_MAGIC_NUM) {
return (TSAL_PCP_ERROR);
}
/* read version field */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* check protocol version */
if (proto_ver != PCP_PROT_VER_1) {
return (TSAL_PCP_ERROR);
}
/* Read message type */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* Read message sub type */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* Read rcvd_pad bits */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* receive transaction id */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* receive timeout value */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* receive message length */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* receive status field */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* receive message checksum */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* receive header checksum */
PCP_IO_OP_READ)) != 0) {
return (ret);
}
/* copy to resp_hdr */
return (TSAL_PCP_OK);
}
/*
* Get next xid for including in request message.
* Every request and response message are matched
* for same xid.
*/
static uint32_t
{
if (xid_initialized == B_FALSE) {
/*
* starting xid is initialized to a different value everytime
* user application is restarted so that user apps will not
* receive previous session's packets.
*
* Note: The algorithm for generating initial xid is partially
* taken from Solaris rpc code.
*/
}
/* zero xid is not allowed */
if (ret == 0)
return (ret);
}
/*
* This function handles channel framing errors. It waits until proper
* frame with starting sequence as magic numder (0xAFBCAFA0)
* is arrived. It removes unexpected data (before the magic number sequence)
* on the channel. It returns when proper magic number sequence is seen
*/
static int
{
int ispresent = 0;
while (!ispresent) {
/*
* Check if next four bytes matches pcp magic number.
* if mathing not found, discard 1 byte and continue checking.
*/
&ispresent)) {
if (!ispresent) {
/* remove 1 byte */
}
} else {
return (-1);
}
}
return (0);
}
/*
* checks whether certain byte sequence is present in the data stream.
*/
static int
{
int ret, i;
return (ret);
}
/* 'byte_cnt' bytes not present */
*ispresent = 0;
return (0);
}
for (i = 0; i < byte_cnt; ++i) {
*ispresent = 0;
return (0);
}
}
*ispresent = 1;
return (0);
}
/*
* 16-bit simple internet checksum
*/
static uint16_t
{
/*
* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
while (count > 1) {
/* This is the inner loop */
count -= 2;
}
/* Add left-over byte, if any */
if (count > 0)
/* Fold 32-bit sum to 16 bits */
while (sum >> 16)
if (sum == 0)
sum = 0xffff;
return (sum);
}