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
972 lines
27 KiB
4 years ago
|
/*
|
||
|
* $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: */
|
||
|
|