You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

972 lines
27 KiB

/*
* $Id: padp.c,v 1.59 2006/10/13 09:52:13 fpillet Exp $
*
* padp.c: Pilot PADP protocol
*
* (c) 1996, D. Jeff Dionne.
* Much of this code adapted from Brian J. Swetland <swetland@uiuc.edu>
* Mostly rewritten by Kenneth Albanowski. Adjusted timeout values and
* better error handling by Tilo Christ.
* (c) 2005, Florent Pillet
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
* General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include "pi-debug.h"
#include "pi-source.h"
#include "pi-padp.h"
#include "pi-slp.h"
#include "pi-error.h"
#define PI_PADP_TX_TIMEOUT 2*1000
#define PI_PADP_TX_RETRIES 10
#define PI_PADP_RX_BLOCK_TO 30*1000
#define PI_PADP_RX_SEGMENT_TO 30*1000
/* Declare function prototypes */
static int padp_flush(pi_socket_t *ps, int flags);
static int padp_getsockopt(pi_socket_t *ps, int level, int option_name,
void *option_value, size_t *option_len);
static int padp_setsockopt(pi_socket_t *ps, int level, int option_name,
const void *option_value, size_t *option_len);
static int padp_sendack(struct pi_socket *ps, struct pi_padp_data *data,
unsigned char txid, struct padp *padp, int flags);
/***********************************************************************
*
* Function: padp_protocol_dup
*
* Summary: clones an existing pi_protocol struct
*
* Parameters: pi_protocol*
*
* Returns: pi_protocol_t* or NULL if operation failed
*
***********************************************************************/
static pi_protocol_t*
padp_protocol_dup (pi_protocol_t *prot)
{
pi_protocol_t *new_prot = NULL;
pi_padp_data_t *data = NULL,
*new_data = NULL;
new_prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
if (new_prot != NULL) {
new_data = (pi_padp_data_t *)malloc (sizeof (pi_padp_data_t));
if (new_data == NULL) {
free(new_prot);
new_prot = NULL;
} else {
new_prot->level = prot->level;
new_prot->dup = prot->dup;
new_prot->free = prot->free;
new_prot->read = prot->read;
new_prot->write = prot->write;
new_prot->flush = prot->flush;
new_prot->getsockopt = prot->getsockopt;
new_prot->setsockopt = prot->setsockopt;
data = (pi_padp_data_t *)prot->data;
memcpy(new_data, data, sizeof(pi_padp_data_t));
new_prot->data = new_data;
}
}
return new_prot;
}
/***********************************************************************
*
* Function: padp_protocol_free
*
* Summary: frees an existing pi_protocol struct
*
* Parameters: pi_protocol*
*
* Returns: void
*
***********************************************************************/
static
void padp_protocol_free (pi_protocol_t *prot)
{
ASSERT (prot != NULL);
if (prot != NULL) {
if (prot->data != NULL)
free(prot->data);
free(prot);
}
}
/***********************************************************************
*
* Function: padp_protocol
*
* Summary: creates and inits pi_protocol struct instance
*
* Parameters: void
*
* Returns: pi_protocol_t* or NULL if operation failed
*
***********************************************************************/
pi_protocol_t
*padp_protocol (void)
{
pi_protocol_t *prot = NULL;
pi_padp_data_t *data = NULL;
prot = (pi_protocol_t *) malloc (sizeof (pi_protocol_t));
if (prot != NULL) {
data = (pi_padp_data_t *) malloc (sizeof (pi_padp_data_t));
if (data == NULL) {
free(prot);
prot = NULL;
} else {
prot->level = PI_LEVEL_PADP;
prot->dup = padp_protocol_dup;
prot->free = padp_protocol_free;
prot->read = padp_rx;
prot->write = padp_tx;
prot->flush = padp_flush;
prot->getsockopt = padp_getsockopt;
prot->setsockopt = padp_setsockopt;
data->type = padData;
data->last_type = -1;
data->txid = 0xff;
data->next_txid = 0xff;
data->freeze_txid = 0;
data->use_long_format = 0;
prot->data = data;
}
}
return prot;
}
/***********************************************************************
*
* Function: padp_tx
*
* Summary: Transmit PADP packets
*
* Parameters: pi_socket_t*, char* to buffer, buffer length, flags
*
* Returns: Number of packets transmitted
*
***********************************************************************/
ssize_t
padp_tx(pi_socket_t *ps, const unsigned char *buf, size_t len, int flags)
{
int fl = PADP_FL_FIRST,
count = 0,
retries,
result,
type,
socket,
timeout,
header_size;
size_t size,
tlen;
unsigned char txid;
pi_protocol_t *prot, *next;
pi_padp_data_t *data;
pi_buffer_t *padp_buf;
struct padp padp;
prot = pi_protocol(ps->sd, PI_LEVEL_PADP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (pi_padp_data_t *)prot->data;
next = pi_protocol_next(ps->sd, PI_LEVEL_PADP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
if (data->type == padWake)
data->txid = 0xff;
if (!data->freeze_txid) {
if (data->txid == 0)
data->txid = 0x10; /* some random # */
else if (data->txid >= 0xfe)
data->next_txid = 1; /* wrap */
else
data->next_txid = data->txid + 1;
}
if (data->type != padAck && ps->state == PI_SOCK_CONN_ACCEPT)
data->txid = data->next_txid;
padp_buf = pi_buffer_new (PI_PADP_HEADER_LEN + 2 + PI_PADP_MTU);
if (padp_buf == NULL)
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
pi_flush(ps->sd, PI_FLUSH_INPUT);
do {
retries = PI_PADP_TX_RETRIES;
do {
padp_buf->used = 0;
type = PI_SLP_TYPE_PADP;
socket = PI_SLP_SOCK_DLP;
timeout = PI_PADP_TX_TIMEOUT;
size = sizeof(type);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_TYPE, &type, &size);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_DEST, &socket, &size);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_SRC, &socket, &size);
size = sizeof(timeout);
pi_setsockopt(ps->sd, PI_LEVEL_DEV, PI_DEV_TIMEOUT, &timeout, &size);
size = sizeof(data->txid);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_TXID, &data->txid, &size);
tlen = (len > PI_PADP_MTU) ? PI_PADP_MTU : len;
header_size = data->use_long_format ? PI_PADP_HEADER_LEN+2 : PI_PADP_HEADER_LEN;
/* build the packet */
set_byte(&padp_buf->data[PI_PADP_OFFSET_TYPE], data->type);
set_byte(&padp_buf->data[PI_PADP_OFFSET_FLGS], fl |
(len == tlen ? PADP_FL_LAST : 0) |
(data->use_long_format ? PADP_FL_LONG : 0));
if (data->use_long_format)
set_long(&padp_buf->data[PI_PADP_OFFSET_SIZE], (fl ? len : (size_t)count));
else
set_short(&padp_buf->data[PI_PADP_OFFSET_SIZE], (fl ? len : (size_t)count));
memcpy(padp_buf->data + header_size, buf, tlen);
CHECK(PI_DBG_PADP, PI_DBG_LVL_INFO, padp_dump_header(padp_buf->data, 1));
CHECK(PI_DBG_PADP, PI_DBG_LVL_DEBUG, padp_dump(padp_buf->data));
/* send the packet, check for disconnection (i.e. when running over USB) */
result = next->write(ps, padp_buf->data, header_size + tlen, flags);
if (result < 0) {
if (result == PI_ERR_SOCK_DISCONNECTED)
goto disconnected;
}
/* Tickles don't get acks */
if (data->type == padTickle)
break;
keepwaiting:
LOG((PI_DBG_PADP, PI_DBG_LVL_DEBUG, "PADP TX waiting for ACK\n"));
result = next->read(ps, padp_buf, PI_PADP_HEADER_LEN + 2 + PI_PADP_MTU, flags);
if (result > 0) {
padp.type = get_byte(&padp_buf->data[PI_PADP_OFFSET_TYPE]);
padp.flags = get_byte(&padp_buf->data[PI_PADP_OFFSET_FLGS]);
if (padp.flags & PADP_FL_LONG) {
header_size = PI_PADP_HEADER_LEN + 2;
padp.size = get_long(&padp_buf->data[PI_PADP_OFFSET_SIZE]);
} else {
header_size = PI_PADP_HEADER_LEN;
padp.size = get_short(&padp_buf->data[PI_PADP_OFFSET_SIZE]);
}
CHECK(PI_DBG_PADP, PI_DBG_LVL_INFO, padp_dump_header(padp_buf->data, 0));
CHECK(PI_DBG_PADP, PI_DBG_LVL_DEBUG, padp_dump(padp_buf->data));
size = sizeof(type);
pi_getsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_LASTTYPE, &type, &size);
size = sizeof(txid);
pi_getsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_LASTTXID, &txid, &size);
if (type == PI_SLP_TYPE_PADP
&& padp.type == (unsigned char)padData
&& txid == data->txid
&& len == tlen) {
/* We didn't receive the ack for the
last packet, but the incomding data
is the response to this transmission.
The ack was lost.
*/
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP TX Missing Ack\n"));
count += tlen;
goto done;
} else if (padp.type == (unsigned char)padTickle) {
/* Tickle to avoid timeout */
goto keepwaiting;
} else if (type == PI_SLP_TYPE_PADP &&
padp.type == (unsigned char)padAck &&
txid == data->txid) {
if (padp.flags & PADP_FL_MEMERROR) {
/* OS 2.x enjoys sending erroneous memory errors */
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP TX Memory Error\n"));
/* Mimimum failure: transmission failed due to
* lack of memory in reciever link layer, but
* connection is still active. This transmission
* was lost, but other transmissions will be
* received.
*/
errno = EMSGSIZE;
count = -1;
goto done;
}
/* Successful Ack */
buf = buf + tlen;
len -= tlen;
count += tlen;
fl = 0;
LOG((PI_DBG_PADP, PI_DBG_LVL_DEBUG, "PADP TX got ACK\n"));
break;
} else if (type == PI_SLP_TYPE_PADP &&
padp.type == data->last_ack_padp.type &&
padp.flags == data->last_ack_padp.flags &&
padp.size == data->last_ack_padp.size &&
txid == data->last_ack_txid) {
/* A repeat of a packet we already received. The
ack got lost, so resend it. */
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP TX resending lost ACK\n"));
padp_sendack(ps, data, txid, &padp, flags);
continue;
} else {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR,
"PADP TX Unexpected packet "
"(possible port speed problem? "
"out of sync packet?)\n"));
padp_dump_header (buf, 1);
/* Got unknown packet */
errno = EIO;
count = -1;
goto done;
}
} else if (result == PI_ERR_SOCK_DISCONNECTED)
goto disconnected;
} while (--retries > 0);
if (retries == 0) {
/* Maximum failure: transmission
failed, and the connection must be presumed dead */
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR, "PADP TX too many retries"));
errno = ETIMEDOUT;
pi_buffer_free (padp_buf);
ps->state = PI_SOCK_CONN_BREAK;
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
} while (len);
done:
if (data->type != padAck && ps->state == PI_SOCK_CONN_INIT)
data->txid = data->next_txid;
pi_buffer_free (padp_buf);
return count;
disconnected:
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR, "PADP TX disconnected"));
pi_buffer_free(padp_buf);
ps->state = PI_SOCK_CONN_BREAK;
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
/***********************************************************************
*
* Function: padp_rx
*
* Summary: Receive PADP packets
*
* Parameters: pi_socket_t*, char* to buffer, expected length, flags
*
* Returns: number of bytes received or negative on error
*
***********************************************************************/
ssize_t
padp_rx(pi_socket_t *ps, pi_buffer_t *buf, size_t expect, int flags)
{
int bytes,
offset = 0,
ouroffset = 0,
honor_rx_timeout,
type,
timeout,
header_size;
unsigned char txid;
size_t total_bytes,
size;
pi_protocol_t *next, *prot;
pi_padp_data_t *data;
struct padp padp;
pi_buffer_t *padp_buf;
time_t endtime;
LOG((PI_DBG_PADP, PI_DBG_LVL_DEBUG, "PADP RX expect=%d flags=0x%04x\n",
expect, flags));
prot = pi_protocol(ps->sd, PI_LEVEL_PADP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (pi_padp_data_t *)prot->data;
next = pi_protocol_next(ps->sd, PI_LEVEL_PADP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
size = sizeof(honor_rx_timeout);
pi_getsockopt(ps->sd, PI_LEVEL_SOCK, PI_SOCK_HONOR_RX_TIMEOUT,
&honor_rx_timeout, &size);
padp_buf = pi_buffer_new (PI_PADP_HEADER_LEN + PI_PADP_MTU);
if (padp_buf == NULL) {
errno = ENOMEM;
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
}
/* the txid may be "frozen" if we're in the process of doing a large read
* over VFS. In this case, all packets have the same txid
*/
if (!data->freeze_txid) {
if (ps->state == PI_SOCK_CONN_ACCEPT) {
if (data->txid >= 0xfe)
data->next_txid = 1; /* wrap */
else
data->next_txid = data->txid + 1;
} else
data->next_txid = data->txid;
}
endtime = time(NULL) + PI_PADP_RX_BLOCK_TO / 1000;
for (;;) {
if (honor_rx_timeout && time(NULL) > endtime) {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR,
"PADP RX Timed out"));
/* Bad timeout breaks connection */
errno = ETIMEDOUT;
ps->state = PI_SOCK_CONN_BREAK;
pi_buffer_free (padp_buf);
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
timeout = honor_rx_timeout ? PI_PADP_RX_BLOCK_TO + 2000 : 0;
size = sizeof(timeout);
pi_setsockopt(ps->sd, PI_LEVEL_DEV, PI_DEV_TIMEOUT,
&timeout, &size);
total_bytes = 0;
padp_buf->used = 0;
header_size = PI_PADP_HEADER_LEN;
while (total_bytes < header_size) {
bytes = next->read(ps, padp_buf,
(size_t)header_size + PI_PADP_MTU - total_bytes, flags);
if (bytes < 0) {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR, "PADP RX Read Error\n"));
pi_buffer_free (padp_buf);
return bytes;
}
total_bytes += bytes;
/* check for the long packet format flag and adjust header size */
if (header_size==PI_PADP_HEADER_LEN &&
total_bytes >= PI_PADP_HEADER_LEN &&
(padp_buf->data[PI_PADP_OFFSET_FLGS] & PADP_FL_LONG)) {
header_size += 2;
}
}
padp.type = padp_buf->data[PI_PADP_OFFSET_TYPE];
padp.flags = padp_buf->data[PI_PADP_OFFSET_FLGS];
if (padp.flags & PADP_FL_LONG)
padp.size = get_long(&padp_buf->data[PI_PADP_OFFSET_SIZE]);
else
padp.size = get_short(&padp_buf->data[PI_PADP_OFFSET_SIZE]);
size = sizeof(type);
pi_getsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_LASTTYPE, &type, &size);
size = sizeof(txid);
pi_getsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_LASTTXID, &txid, &size);
if (padp.flags & PADP_FL_MEMERROR) {
if (txid == data->txid) {
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP RX Memory Error\n"));
errno = EMSGSIZE;
ouroffset = -1;
goto done; /* Mimimum failure:
transmission failed due to
lack of memory in reciever
link layer, but connection is
still active. This
transmission was lost, but
other transmissions will be
received. */
}
continue;
} else if (padp.type == padTickle) {
/* Tickle to avoid timeout */
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP RX Got Tickled\n"));
endtime = time(NULL) + PI_PADP_RX_BLOCK_TO / 1000;
continue;
} else if (type != PI_SLP_TYPE_PADP ||
padp.type != padData ||
txid != data->txid ||
!(padp.flags & PADP_FL_FIRST)) {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR,
"PADP RX Wrong packet type on queue"
"(possible port speed problem? (loc1))\n"));
continue;
}
break;
}
/* OK, we got the expected begin-of-data packet */
endtime = time(NULL) + PI_PADP_RX_SEGMENT_TO / 1000;
for (;;) {
CHECK(PI_DBG_PADP, PI_DBG_LVL_INFO, padp_dump_header(padp_buf->data, 0));
CHECK(PI_DBG_PADP, PI_DBG_LVL_DEBUG, padp_dump(padp_buf->data));
/* Ack the packet */
padp_sendack(ps, data, data->txid, &padp, flags);
/* calculate length and offset - remove */
offset = ((padp.flags & PADP_FL_FIRST) ? 0 : padp.size);
total_bytes -= PI_PADP_HEADER_LEN;
/* If packet was out of order, ignore it */
if (offset == ouroffset) {
if (pi_buffer_append (buf, &padp_buf->data[header_size], total_bytes) == NULL) {
errno = ENOMEM;
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
}
ouroffset += total_bytes;
}
if (padp.flags & PADP_FL_LAST)
break;
endtime = time(NULL) + PI_PADP_RX_SEGMENT_TO / 1000;
for (;;) {
if (honor_rx_timeout && time(NULL) > endtime) {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR,
"PADP RX Segment Timeout"));
/* Segment timeout, return error */
errno = ETIMEDOUT;
ouroffset = -1;
/* Bad timeout breaks connection */
ps->state = PI_SOCK_CONN_BREAK;
pi_buffer_free (padp_buf);
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
timeout = honor_rx_timeout ? (PI_PADP_RX_SEGMENT_TO + 2000) : 0;
size = sizeof(timeout);
pi_setsockopt(ps->sd, PI_LEVEL_DEV, PI_DEV_TIMEOUT, &timeout, &size);
total_bytes = 0;
padp_buf->used = 0;
header_size = PI_PADP_HEADER_LEN;
while (total_bytes < header_size) {
bytes = next->read(ps, padp_buf,
header_size + PI_PADP_MTU - total_bytes, flags);
if (bytes < 0) {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR, "PADP RX Read Error"));
pi_buffer_free (padp_buf);
return pi_set_error(ps->sd, bytes);
}
total_bytes += bytes;
/* check for the long packet format flag and adjust header size */
if (header_size==PI_PADP_HEADER_LEN &&
total_bytes >= PI_PADP_HEADER_LEN &&
(padp_buf->data[PI_PADP_OFFSET_FLGS] & PADP_FL_LONG)) {
header_size += 2;
}
}
padp.type = padp_buf->data[PI_PADP_OFFSET_TYPE];
padp.flags = padp_buf->data[PI_PADP_OFFSET_FLGS];
if (padp.flags & PADP_FL_LONG)
padp.size = get_long(&padp_buf->data[PI_PADP_OFFSET_SIZE]);
else
padp.size = get_short(&padp_buf->data[PI_PADP_OFFSET_SIZE]);
CHECK(PI_DBG_PADP, PI_DBG_LVL_INFO, padp_dump_header(padp_buf->data, 0));
CHECK(PI_DBG_PADP, PI_DBG_LVL_DEBUG, padp_dump(padp_buf->data));
size = sizeof(type);
pi_getsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_LASTTYPE, &type, &size);
size = sizeof(txid);
pi_getsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_LASTTXID, &txid, &size);
if (padp.flags & PADP_FL_MEMERROR) {
if (txid == data->txid) {
/* Mimimum failure: transmission failed due
to lack of memory in receiver link layer,
but connection is still active. This
transmission was lost, but other transmissions
will be received. */
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP RX Memory Error"));
errno = EMSGSIZE;
ouroffset = -1;
goto done;
}
continue;
}
if (padp.type == (unsigned char) 4) {
/* Tickle to avoid timeout */
endtime = time(NULL) +
PI_PADP_RX_BLOCK_TO / 1000;
LOG((PI_DBG_PADP, PI_DBG_LVL_WARN,
"PADP RX Got Tickled"));
continue;
}
if (type != PI_SLP_TYPE_PADP ||
padp.type != padData ||
txid != data->txid ||
(padp.flags & PADP_FL_FIRST)) {
LOG((PI_DBG_PADP, PI_DBG_LVL_ERR,
"PADP RX Wrong packet type on queue"
"(possible port speed problem? (loc2))\n"));
continue;
}
break;
}
}
done:
data->txid = data->next_txid;
pi_buffer_free (padp_buf);
return ouroffset;
}
/***********************************************************************
*
* Function: padp_flush
*
* Summary: Flush input and output buffers
*
* Parameters: pi_socket_t*, flags
*
* Returns: A negative number on error, 0 otherwise
*
***********************************************************************/
static int
padp_flush(pi_socket_t *ps, int flags)
{
pi_protocol_t *prot,
*next;
prot = pi_protocol(ps->sd, PI_LEVEL_PADP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
next = pi_protocol_next(ps->sd, PI_LEVEL_PADP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
return next->flush(ps, flags);
}
/***********************************************************************
*
* Function: padp_getsockopt
*
* Summary: get options on socket
*
* Parameters: pi_socket*, level, option name, option value, option length
*
* Returns: 0 for success, negative otherwise
*
***********************************************************************/
static int
padp_getsockopt(pi_socket_t *ps, int level, int option_name,
void *option_value, size_t *option_len)
{
pi_protocol_t *prot;
pi_padp_data_t *data;
prot = pi_protocol(ps->sd, PI_LEVEL_PADP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (pi_padp_data_t *)prot->data;
switch (option_name) {
case PI_PADP_TYPE:
if (*option_len != sizeof (data->type))
goto error;
memcpy (option_value, &data->type, sizeof (data->type));
break;
case PI_PADP_LASTTYPE:
if (*option_len != sizeof (data->last_type))
goto error;
memcpy (option_value, &data->last_type, sizeof (data->last_type));
break;
case PI_PADP_FREEZE_TXID:
if (*option_len != sizeof (data->freeze_txid))
goto error;
memcpy (option_value, &data->freeze_txid, sizeof(data->freeze_txid));
break;
case PI_PADP_USE_LONG_FORMAT:
if (*option_len != sizeof (data->use_long_format))
goto error;
memcpy (option_value, &data->use_long_format, sizeof(data->use_long_format));
break;
}
return 0;
error:
errno = EINVAL;
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
}
/***********************************************************************
*
* Function: padp_setsockopt
*
* Summary: get options on socket
*
* Parameters: pi_socket*, level, option name, option value, option length
*
* Returns: 0 for success, negative otherwise
*
***********************************************************************/
static int
padp_setsockopt(pi_socket_t *ps, int level, int option_name,
const void *option_value, size_t *option_len)
{
pi_protocol_t *prot;
pi_padp_data_t *data;
int was_frozen;
prot = pi_protocol(ps->sd, PI_LEVEL_PADP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (pi_padp_data_t *)prot->data;
switch (option_name) {
case PI_PADP_TYPE:
if (*option_len != sizeof (data->type))
goto error;
memcpy (&data->type, option_value, sizeof (data->type));
break;
case PI_PADP_FREEZE_TXID:
if (*option_len != sizeof (data->freeze_txid))
goto error;
was_frozen = data->freeze_txid;
memcpy (&data->freeze_txid, option_value, sizeof (data->freeze_txid));
if (was_frozen && !data->freeze_txid) {
data->next_txid++;
if (data->next_txid >= 0xfe)
data->next_txid = 1;
}
break;
case PI_PADP_USE_LONG_FORMAT:
if (*option_len != sizeof (data->use_long_format))
goto error;
memcpy (&data->use_long_format, option_value, sizeof(data->use_long_format));
break;
}
return 0;
error:
errno = EINVAL;
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
}
/***********************************************************************
*
* Function: padp_sendack
*
* Summary: Acknowledge receipt of a packet
*
* Parameters:
*
* Returns:
*
***********************************************************************/
static int
padp_sendack(struct pi_socket *ps,
struct pi_padp_data *data, /* padp state, will be modified */
unsigned char txid, /* txid of the packet being acked */
struct padp *padp, /* padp header of the packet being acked */
int flags)
{
int type,
socket,
result,
header_size;
size_t size;
unsigned char
npadp_buf[PI_PADP_HEADER_LEN+2];
struct pi_protocol
*next;
next = pi_protocol_next(ps->sd, PI_LEVEL_PADP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
type = 2;
socket = PI_SLP_SOCK_DLP;
size = sizeof(type);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_TYPE, &type, &size);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_DEST, &socket, &size);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_SRC, &socket, &size);
size = sizeof(txid);
pi_setsockopt(ps->sd, PI_LEVEL_SLP, PI_SLP_TXID, &txid, &size);
header_size = PI_PADP_HEADER_LEN;
set_byte(&npadp_buf[PI_PADP_OFFSET_TYPE], padAck);
set_byte(&npadp_buf[PI_PADP_OFFSET_FLGS], padp->flags);
if (padp->flags & PADP_FL_LONG) {
header_size += 2;
set_long(&npadp_buf[PI_PADP_OFFSET_SIZE], padp->size);
} else {
set_short(&npadp_buf[PI_PADP_OFFSET_SIZE], padp->size);
}
CHECK(PI_DBG_PADP, PI_DBG_LVL_INFO, padp_dump_header(npadp_buf, 1));
CHECK(PI_DBG_PADP, PI_DBG_LVL_DEBUG, padp_dump(npadp_buf));
result = next->write(ps, npadp_buf, header_size, flags);
if (result >= 0) {
data->last_ack_txid = txid;
data->last_ack_padp.type = padp->type;
data->last_ack_padp.flags = padp->flags;
data->last_ack_padp.size = padp->size;
}
return result;
}
/***********************************************************************
*
* Function: padp_dump_header
*
* Summary: Dump PADP packet header
*
* Parameters: char* to data, RXTX boolean
*
* Returns: void
*
***********************************************************************/
void
padp_dump_header(const unsigned char *data, int rxtx)
{
long s;
char *stype;
unsigned char type, flags;
type = get_byte (&data[PI_PADP_OFFSET_TYPE]);
switch (type) {
case padData:
stype = "DATA";
break;
case padAck:
stype = "ACK";
break;
case padTickle:
stype = "TICKLE";
break;
case padAbort:
stype = "ABORT";
break;
default:
stype = "UNK";
break;
}
flags = get_byte(&data[PI_PADP_OFFSET_FLGS]);
if (flags & PADP_FL_LONG)
s = get_long(&data[PI_PADP_OFFSET_SIZE]);
else
s = get_short(&data[PI_PADP_OFFSET_SIZE]);
LOG((PI_DBG_PADP, PI_DBG_LVL_NONE,
"PADP %s %c%c%c type=%s len=%ld\n",
rxtx ? "TX" : "RX",
(flags & PADP_FL_FIRST) ? 'F' : ' ',
(flags & PADP_FL_LAST) ? 'L' : ' ',
(flags & PADP_FL_MEMERROR) ? 'M' : ' ',
stype, s));
}
/***********************************************************************
*
* Function: padp_dump
*
* Summary: Dump PADP packets
*
* Parameters: char* to data
*
* Returns: void
*
***********************************************************************/
void
padp_dump(const unsigned char *data)
{
size_t size;
unsigned char
type,
flags;
int header_size = PI_PADP_HEADER_LEN;
type = get_byte (&data[PI_PADP_OFFSET_TYPE]);
flags = get_byte (&data[PI_PADP_OFFSET_FLGS]);
if (flags & PADP_FL_LONG) {
header_size += 2;
size = get_long(&data[PI_PADP_OFFSET_SIZE]);
} else
size = get_short(&data[PI_PADP_OFFSET_SIZE]);
if (size > PI_PADP_MTU)
size = PI_PADP_MTU;
if (type != padAck)
pi_dumpdata((char *)&data[header_size], size);
}
/* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */
/* ex: set tabstop=4 expandtab: */
/* Local Variables: */
/* indent-tabs-mode: t */
/* c-basic-offset: 8 */
/* End: */