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.
716 lines
19 KiB
716 lines
19 KiB
4 years ago
|
/*
|
||
|
* $Id: net.c,v 1.45 2006/10/12 14:21:22 desrod Exp $
|
||
|
*
|
||
|
* net.c: Protocol for NetSync connections
|
||
|
*
|
||
|
* Copyright (c) 1997, Kenneth Albanowski
|
||
|
* Copyright (c) 1999, Tilo Christ
|
||
|
* Copyright (c) 1999, John Franks
|
||
|
* Copyright (c) 2004, 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 <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "pi-debug.h"
|
||
|
#include "pi-source.h"
|
||
|
#include "pi-net.h"
|
||
|
#include "pi-error.h"
|
||
|
|
||
|
#define PI_NET_TIMEOUT 30*1000
|
||
|
|
||
|
static int net_flush(pi_socket_t *ps, int flags);
|
||
|
static int net_getsockopt(pi_socket_t *ps, int level, int option_name,
|
||
|
void *option_value, size_t *option_len);
|
||
|
static int net_setsockopt(pi_socket_t *ps, int level, int option_name,
|
||
|
const void *option_value, size_t *option_len);
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_protocol_dup
|
||
|
*
|
||
|
* Summary: clones an existing pi_protocol struct
|
||
|
*
|
||
|
* Parameters: pi_protocol_t*
|
||
|
*
|
||
|
* Returns: pi_protocol_t* or NULL if operation failed
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static pi_protocol_t
|
||
|
*net_protocol_dup (pi_protocol_t *prot)
|
||
|
{
|
||
|
pi_protocol_t *new_prot = NULL;
|
||
|
pi_net_data_t *data = NULL,
|
||
|
*new_data = NULL;
|
||
|
|
||
|
ASSERT(prot != NULL);
|
||
|
|
||
|
new_prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
|
||
|
if (new_prot != NULL) {
|
||
|
new_data = (pi_net_data_t *)malloc (sizeof (pi_net_data_t));
|
||
|
if (new_data == NULL) {
|
||
|
free(new_prot);
|
||
|
new_prot = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (new_prot != NULL && new_data != NULL) {
|
||
|
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_net_data_t *)prot->data;
|
||
|
new_data->type = data->type;
|
||
|
new_data->split_writes = data->split_writes;
|
||
|
new_data->write_chunksize = data->write_chunksize;
|
||
|
new_data->txid = data->txid;
|
||
|
new_prot->data = new_data;
|
||
|
}
|
||
|
|
||
|
return new_prot;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_protocol_free
|
||
|
*
|
||
|
* Summary: frees an existing pi_protocol struct
|
||
|
*
|
||
|
* Parameters: pi_protocol*
|
||
|
*
|
||
|
* Returns: void
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static
|
||
|
void net_protocol_free (pi_protocol_t *prot)
|
||
|
{
|
||
|
ASSERT (prot != NULL);
|
||
|
|
||
|
if (prot != NULL) {
|
||
|
if (prot->data != NULL)
|
||
|
free(prot->data);
|
||
|
free(prot);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_protocol
|
||
|
*
|
||
|
* Summary: creates and inits pi_protocol struct instance
|
||
|
*
|
||
|
* Parameters: void
|
||
|
*
|
||
|
* Returns: pi_protocol_t* or NULL if operation failed
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
pi_protocol_t
|
||
|
*net_protocol (void)
|
||
|
{
|
||
|
pi_protocol_t *prot = NULL;
|
||
|
pi_net_data_t *data = NULL;
|
||
|
|
||
|
prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
|
||
|
if (prot != NULL) {
|
||
|
data = (pi_net_data_t *)malloc (sizeof (pi_net_data_t));
|
||
|
if (data == NULL) {
|
||
|
free(prot);
|
||
|
prot = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (prot != NULL && data != NULL) {
|
||
|
prot->level = PI_LEVEL_NET;
|
||
|
prot->dup = net_protocol_dup;
|
||
|
prot->free = net_protocol_free;
|
||
|
prot->read = net_rx;
|
||
|
prot->write = net_tx;
|
||
|
prot->flush = net_flush;
|
||
|
prot->getsockopt = net_getsockopt;
|
||
|
prot->setsockopt = net_setsockopt;
|
||
|
|
||
|
data->type = PI_NET_TYPE_DATA;
|
||
|
data->split_writes = 1; /* write packet header and data separately */
|
||
|
data->write_chunksize = 4096; /* and push data in 4k chunks. Required for some USB devices */
|
||
|
data->txid = 0x00;
|
||
|
prot->data = data;
|
||
|
}
|
||
|
|
||
|
return prot;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_rx_handshake
|
||
|
*
|
||
|
* Summary: RX handshake
|
||
|
*
|
||
|
* Parameters: pi_socket_t*
|
||
|
*
|
||
|
* Returns: 0 for success, negative otherwise
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
int
|
||
|
net_rx_handshake(pi_socket_t *ps)
|
||
|
{
|
||
|
static const unsigned char msg1[] = /* 50 bytes */
|
||
|
"\x12\x01\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00"
|
||
|
"\x24\xff\xff\xff\xff\x3c\x00\x3c\x00\x00\x00\x00\x00"
|
||
|
"\x00\x00\x00\x00\xc0\xa8\xa5\x1f\x04\x27\x00\x00\x00"
|
||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||
|
static const unsigned char msg2[] = /* 46 bytes */
|
||
|
"\x13\x01\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00"
|
||
|
"\x20\xff\xff\xff\xff\x00\x3c\x00\x3c\x00\x00\x00\x00"
|
||
|
"\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||
|
"\x00\x00\x00\x00\x00\x00\x00";
|
||
|
pi_buffer_t *buffer;
|
||
|
int err;
|
||
|
|
||
|
buffer = pi_buffer_new (256);
|
||
|
if (buffer == NULL) {
|
||
|
errno = ENOMEM;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
|
||
|
}
|
||
|
|
||
|
if ((err = net_rx(ps, buffer, 256, 0)) >= 0 &&
|
||
|
(err = net_tx(ps, msg1, 50, 0)) >= 0 &&
|
||
|
(err = net_rx(ps, buffer, 50, 0)) >= 0 &&
|
||
|
(err = net_tx(ps, msg2, 46, 0)) >= 0 &&
|
||
|
(err = net_rx(ps, buffer, 8, 0)) >= 0)
|
||
|
{
|
||
|
pi_buffer_free (buffer);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
pi_buffer_free (buffer);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_tx_handshake
|
||
|
*
|
||
|
* Summary: TX handshake
|
||
|
*
|
||
|
* Parameters: pi_socket_t*
|
||
|
*
|
||
|
* Returns: 0 for success, negative otherwise
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
int
|
||
|
net_tx_handshake(pi_socket_t *ps)
|
||
|
{
|
||
|
static const unsigned char msg1[] = /* 22 bytes */
|
||
|
"\x90\x01\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00"
|
||
|
"\x08\x01\x00\x00\x00\x00\x00\x00\x00";
|
||
|
static const unsigned char msg2[] = /* 50 bytes */
|
||
|
"\x92\x01\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00"
|
||
|
"\x24\xff\xff\xff\xff\x00\x3c\x00\x3c\x40\x00\x00\x00"
|
||
|
"\x01\x00\x00\x00\xc0\xa8\xa5\x1e\x04\x01\x00\x00\x00"
|
||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||
|
static const unsigned char msg3[] = /* 8 bytes */
|
||
|
"\x93\x00\x00\x00\x00\x00\x00\x00";
|
||
|
pi_buffer_t *buffer;
|
||
|
int err;
|
||
|
|
||
|
buffer = pi_buffer_new (256);
|
||
|
if (buffer == NULL) {
|
||
|
errno = ENOMEM;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
|
||
|
}
|
||
|
|
||
|
if ((err = net_tx(ps, msg1, 22, 0)) >= 0 &&
|
||
|
(err = net_rx(ps, buffer, 256, 0)) >= 0 &&
|
||
|
(err = net_tx(ps, msg2, 50, 0)) >= 0 &&
|
||
|
(err = net_rx(ps, buffer, 256, 0)) >= 0 &&
|
||
|
(err = net_tx(ps, msg3, 8, 0)) >= 0)
|
||
|
{
|
||
|
pi_buffer_free (buffer);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
pi_buffer_free (buffer);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_flush
|
||
|
*
|
||
|
* Summary: Flush input and output buffers
|
||
|
*
|
||
|
* Parameters: pi_socket_t*, flags
|
||
|
*
|
||
|
* Returns: A negative number on error, 0 otherwise
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
int
|
||
|
net_flush(pi_socket_t *ps, int flags)
|
||
|
{
|
||
|
pi_protocol_t *prot,
|
||
|
*next;
|
||
|
|
||
|
prot = pi_protocol(ps->sd, PI_LEVEL_NET);
|
||
|
if (prot == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
|
||
|
next = pi_protocol_next(ps->sd, PI_LEVEL_NET);
|
||
|
if (next == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
|
||
|
return next->flush(ps, flags);
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_tx
|
||
|
*
|
||
|
* Summary: Transmit NET Packets
|
||
|
*
|
||
|
* Parameters: pi_socket_t*, char* to buf, buf length, flags
|
||
|
*
|
||
|
* Returns: A negative number on error, 0 otherwise
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
ssize_t
|
||
|
net_tx(pi_socket_t *ps, const unsigned char *msg, size_t len, int flags)
|
||
|
{
|
||
|
int bytes,
|
||
|
offset,
|
||
|
remain,
|
||
|
tosend;
|
||
|
pi_protocol_t *prot,
|
||
|
*next;
|
||
|
pi_net_data_t *data;
|
||
|
unsigned char *buf;
|
||
|
|
||
|
prot = pi_protocol(ps->sd, PI_LEVEL_NET);
|
||
|
if (prot == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
data = (pi_net_data_t *)prot->data;
|
||
|
|
||
|
next = pi_protocol_next(ps->sd, PI_LEVEL_NET);
|
||
|
if (next == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
|
||
|
/* Create the header */
|
||
|
buf = (unsigned char *) malloc(PI_NET_HEADER_LEN + len);
|
||
|
if (buf == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
|
||
|
buf[PI_NET_OFFSET_TYPE] = data->type;
|
||
|
if (data->type == PI_NET_TYPE_TCKL)
|
||
|
buf[PI_NET_OFFSET_TXID] = 0xff;
|
||
|
else
|
||
|
buf[PI_NET_OFFSET_TXID] = data->txid;
|
||
|
set_long(&buf[PI_NET_OFFSET_SIZE], len);
|
||
|
memcpy(&buf[PI_NET_HEADER_LEN], msg, len);
|
||
|
|
||
|
/* Write the header and body, possibly in one write, or in two,
|
||
|
* or in more, depending on the current options. Crucial options
|
||
|
* here are `split_writes' and `write_chunksize' in this protocol's
|
||
|
* data (use net_setsockopt() to set them).
|
||
|
*/
|
||
|
if (data->split_writes)
|
||
|
{
|
||
|
/* Bugfix for USB send problems. If connected over
|
||
|
* USB, do the following:
|
||
|
* - send the 6 bytes of header first
|
||
|
* - split the rest of data into chunks if the write_chunksize opt is set
|
||
|
* This is what Palm Desktop does on Windows for the Zire 72
|
||
|
* (uses split writes and 4k chunks)
|
||
|
* -- FP
|
||
|
*/
|
||
|
bytes = next->write(ps, buf, PI_NET_HEADER_LEN, flags);
|
||
|
if (bytes < PI_NET_HEADER_LEN)
|
||
|
{
|
||
|
free(buf);
|
||
|
return bytes;
|
||
|
}
|
||
|
offset = PI_NET_HEADER_LEN;
|
||
|
remain = len;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
offset = 0;
|
||
|
remain = PI_NET_HEADER_LEN + len;
|
||
|
}
|
||
|
|
||
|
while (remain > 0)
|
||
|
{
|
||
|
if (data->write_chunksize)
|
||
|
tosend = (remain > data->write_chunksize) ? data->write_chunksize : remain;
|
||
|
else
|
||
|
tosend = remain;
|
||
|
|
||
|
bytes = next->write(ps, &buf[offset], tosend, flags);
|
||
|
if (bytes < tosend)
|
||
|
{
|
||
|
free(buf);
|
||
|
return bytes;
|
||
|
}
|
||
|
remain -= bytes;
|
||
|
offset += bytes;
|
||
|
}
|
||
|
|
||
|
CHECK(PI_DBG_NET, PI_DBG_LVL_INFO, net_dump_header(buf, 1, ps->sd));
|
||
|
CHECK(PI_DBG_NET, PI_DBG_LVL_DEBUG, pi_dumpdata((char *)msg, len));
|
||
|
|
||
|
free(buf);
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_rx
|
||
|
*
|
||
|
* Summary: Receive NET Packets
|
||
|
*
|
||
|
* Parameters: ps --> socket to read from
|
||
|
* msg <-- malloc()ed buffer containing the data
|
||
|
* len --> unused
|
||
|
* flags --> unused
|
||
|
*
|
||
|
* Returns: A negative number on error, 0 on timeout, otherwise the
|
||
|
* length of the received packet.
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
ssize_t
|
||
|
net_rx(pi_socket_t *ps, pi_buffer_t *msg, size_t len, int flags)
|
||
|
{
|
||
|
int bytes,
|
||
|
total_bytes,
|
||
|
packet_len,
|
||
|
timeout,
|
||
|
honor_rx_timeout;
|
||
|
size_t size;
|
||
|
pi_protocol_t *prot,
|
||
|
*next;
|
||
|
pi_buffer_t *header;
|
||
|
pi_net_data_t *data;
|
||
|
|
||
|
prot = pi_protocol(ps->sd, PI_LEVEL_NET);
|
||
|
if (prot == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
|
||
|
data = (pi_net_data_t *)prot->data;
|
||
|
next = pi_protocol_next(ps->sd, PI_LEVEL_NET);
|
||
|
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);
|
||
|
|
||
|
timeout = honor_rx_timeout ? PI_NET_TIMEOUT : 0;
|
||
|
size = sizeof(timeout);
|
||
|
pi_setsockopt(ps->sd, PI_LEVEL_DEV, PI_DEV_TIMEOUT,
|
||
|
&timeout, &size);
|
||
|
|
||
|
header = pi_buffer_new (PI_NET_HEADER_LEN);
|
||
|
if (header == NULL) {
|
||
|
errno = ENOMEM;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
|
||
|
}
|
||
|
|
||
|
/* loop until we find a non-tickle packet (if the other end
|
||
|
sends us a tickle, we would receive it prior to getting
|
||
|
the expected reply to one of our commands, so we need
|
||
|
to make sure tickle packets don't get in the way) */
|
||
|
total_bytes = 0;
|
||
|
while (!total_bytes) {
|
||
|
if (data->txid == 0) {
|
||
|
/* Peek to see if it is a headerless packet */
|
||
|
bytes = next->read(ps, header, 1, flags);
|
||
|
if (bytes <= 0) {
|
||
|
pi_buffer_free (header);
|
||
|
return bytes;
|
||
|
}
|
||
|
|
||
|
LOG ((PI_DBG_NET, PI_DBG_LVL_INFO,
|
||
|
"NET RX (%i): Checking for headerless packet %d\n",
|
||
|
ps->sd, header->data[0]));
|
||
|
|
||
|
if (header->data[0] == 0x90) {
|
||
|
/* Cause the header bytes to be skipped */
|
||
|
LOG ((PI_DBG_NET, PI_DBG_LVL_INFO,
|
||
|
"NET RX (%i): Headerless packet\n",
|
||
|
ps->sd));
|
||
|
total_bytes = PI_NET_HEADER_LEN;
|
||
|
header->data[PI_NET_OFFSET_TYPE] = PI_NET_TYPE_DATA;
|
||
|
header->data[PI_NET_OFFSET_TXID] = 0x01;
|
||
|
set_long (&header->data[PI_NET_OFFSET_SIZE], 21);
|
||
|
break;
|
||
|
} else {
|
||
|
total_bytes += bytes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* bytes in what's left of the header */
|
||
|
while (total_bytes < PI_NET_HEADER_LEN) {
|
||
|
bytes = next->read(ps, header,
|
||
|
(size_t)(PI_NET_HEADER_LEN - total_bytes), flags);
|
||
|
if (bytes <= 0) {
|
||
|
pi_buffer_free (header);
|
||
|
return bytes;
|
||
|
}
|
||
|
total_bytes += bytes;
|
||
|
}
|
||
|
|
||
|
packet_len = get_long(&header->data[PI_NET_OFFSET_SIZE]);
|
||
|
data->type = header->data[PI_NET_OFFSET_TYPE];
|
||
|
|
||
|
switch (data->type) {
|
||
|
case PI_NET_TYPE_TCKL:
|
||
|
if (packet_len != 0) {
|
||
|
LOG ((PI_DBG_NET, PI_DBG_LVL_ERR,
|
||
|
"NET RX (%i): tickle packet with non-zero length\n",
|
||
|
ps->sd));
|
||
|
pi_buffer_free(header);
|
||
|
return pi_set_error(ps->sd, PI_ERR_PROT_BADPACKET);
|
||
|
}
|
||
|
/* valid tickle packet; continue reading. */
|
||
|
LOG((PI_DBG_NET, PI_DBG_LVL_DEBUG,
|
||
|
"NET RX (%i): received tickle packet\n",
|
||
|
ps->sd));
|
||
|
total_bytes = 0;
|
||
|
header->used = 0;
|
||
|
break;
|
||
|
|
||
|
case PI_NET_TYPE_DATA:
|
||
|
/* move on to reading the rest of the packet */
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LOG ((PI_DBG_NET, PI_DBG_LVL_ERR,
|
||
|
"NET RX (%i): Unknown packet type\n",
|
||
|
ps->sd));
|
||
|
CHECK(PI_DBG_NET, PI_DBG_LVL_INFO, pi_dumpdata((char *)header->data, PI_NET_HEADER_LEN));
|
||
|
pi_buffer_free(header);
|
||
|
return pi_set_error(ps->sd, PI_ERR_PROT_BADPACKET);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
total_bytes = 0;
|
||
|
packet_len = get_long(&header->data[PI_NET_OFFSET_SIZE]);
|
||
|
|
||
|
/* shield against absurd packet lengths */
|
||
|
if (packet_len < 0 || packet_len > 0x100000L) {
|
||
|
/* we see an invalid packet */
|
||
|
next->flush(ps, PI_FLUSH_INPUT);
|
||
|
LOG ((PI_DBG_NET, PI_DBG_LVL_ERR, "NET RX (%i): Invalid packet length (%ld)\n", ps->sd, packet_len));
|
||
|
pi_buffer_free(header);
|
||
|
return pi_set_error(ps->sd, PI_ERR_PROT_BADPACKET);
|
||
|
}
|
||
|
|
||
|
/* read the actual packet data */
|
||
|
while (total_bytes < packet_len) {
|
||
|
bytes = next->read(ps, msg,
|
||
|
(size_t)(packet_len - total_bytes), flags);
|
||
|
if (bytes < 0) {
|
||
|
pi_buffer_free (header);
|
||
|
return bytes;
|
||
|
}
|
||
|
total_bytes += bytes;
|
||
|
}
|
||
|
|
||
|
CHECK(PI_DBG_NET, PI_DBG_LVL_INFO, net_dump_header(header->data, 0, ps->sd));
|
||
|
CHECK(PI_DBG_NET, PI_DBG_LVL_DEBUG, net_dump(header->data, msg->data));
|
||
|
|
||
|
/* Update the transaction id */
|
||
|
if (ps->state == PI_SOCK_CONN_INIT || ps->command == 1)
|
||
|
data->txid = header->data[PI_NET_OFFSET_TXID];
|
||
|
else {
|
||
|
data->txid++;
|
||
|
if (data->txid == 0xff)
|
||
|
data->txid = 1;
|
||
|
}
|
||
|
|
||
|
pi_buffer_free (header);
|
||
|
return packet_len;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_getsockopt
|
||
|
*
|
||
|
* Summary: get options on socket
|
||
|
*
|
||
|
* Parameters: pi_socket*, level, option name, option value, option length
|
||
|
*
|
||
|
* Returns: 0 for success, negative otherwise
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static int
|
||
|
net_getsockopt(pi_socket_t *ps, int level, int option_name,
|
||
|
void *option_value, size_t *option_len)
|
||
|
{
|
||
|
pi_protocol_t *prot;
|
||
|
pi_net_data_t *data;
|
||
|
|
||
|
prot = pi_protocol(ps->sd, PI_LEVEL_NET);
|
||
|
if (prot == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
|
||
|
data = (pi_net_data_t *)prot->data;
|
||
|
|
||
|
switch (option_name) {
|
||
|
case PI_NET_TYPE:
|
||
|
if (*option_len != sizeof (data->type)) {
|
||
|
errno = EINVAL;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
|
||
|
}
|
||
|
memcpy (option_value, &data->type,
|
||
|
sizeof (data->type));
|
||
|
*option_len = sizeof (data->type);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_setsockopt
|
||
|
*
|
||
|
* Summary: set options on socket
|
||
|
*
|
||
|
* Parameters: pi_socket*, level, option name, option value, option length
|
||
|
*
|
||
|
* Returns: 0 for success, negative otherwise
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static int
|
||
|
net_setsockopt(pi_socket_t *ps, int level, int option_name,
|
||
|
const void *option_value, size_t *option_len)
|
||
|
{
|
||
|
pi_protocol_t *prot;
|
||
|
pi_net_data_t *data;
|
||
|
|
||
|
prot = pi_protocol(ps->sd, PI_LEVEL_NET);
|
||
|
if (prot == NULL)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
|
||
|
|
||
|
data = (pi_net_data_t *)prot->data;
|
||
|
|
||
|
switch (option_name) {
|
||
|
case PI_NET_TYPE:
|
||
|
if (*option_len != sizeof (data->type)) {
|
||
|
errno = EINVAL;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
|
||
|
}
|
||
|
memcpy (&data->type, option_value,
|
||
|
sizeof (data->type));
|
||
|
break;
|
||
|
|
||
|
/* this option, when set to != 0, instructs NET to separately
|
||
|
* write the NET header and the data block. Data can be further
|
||
|
* sent in chunks by also setting PI_NET_WRITE_CHUNKSIZE below.
|
||
|
*/
|
||
|
case PI_NET_SPLIT_WRITES:
|
||
|
if (*option_len != sizeof (data->split_writes)) {
|
||
|
errno = EINVAL;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
|
||
|
}
|
||
|
memcpy (&data->split_writes, option_value,
|
||
|
sizeof(data->split_writes));
|
||
|
break;
|
||
|
|
||
|
/* this option, when set to != 0, instructs NET to write the
|
||
|
* packet data in chunks of the given maximum size. If
|
||
|
* PI_NET_SPLIT_WRITES is not set, and this option is set, we
|
||
|
* chunk the whole write (including the NET header)
|
||
|
*/
|
||
|
case PI_NET_WRITE_CHUNKSIZE:
|
||
|
if (*option_len != sizeof (data->write_chunksize)) {
|
||
|
errno = EINVAL;
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
|
||
|
}
|
||
|
memcpy (&data->write_chunksize, option_value,
|
||
|
sizeof(data->write_chunksize));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_dump_header
|
||
|
*
|
||
|
* Summary: Dump the NET packet header
|
||
|
*
|
||
|
* Parameters: char* to net packet, TX boolean
|
||
|
*
|
||
|
* Returns: void
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
void
|
||
|
net_dump_header(unsigned char *data, int rxtx, int sd)
|
||
|
{
|
||
|
LOG((PI_DBG_NET, PI_DBG_LVL_NONE,
|
||
|
"NET %s sd=%i type=%d txid=0x%.2x len=0x%.4x\n",
|
||
|
rxtx ? "TX" : "RX",
|
||
|
sd,
|
||
|
get_byte(&data[PI_NET_OFFSET_TYPE]),
|
||
|
get_byte(&data[PI_NET_OFFSET_TXID]),
|
||
|
get_long(&data[PI_NET_OFFSET_SIZE])));
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: net_dump
|
||
|
*
|
||
|
* Summary: Dump the NET packet
|
||
|
*
|
||
|
* Parameters: char* to net packet
|
||
|
*
|
||
|
* Returns: void
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
void
|
||
|
net_dump(unsigned char *header, unsigned char *data)
|
||
|
{
|
||
|
size_t size;
|
||
|
|
||
|
size = get_long(&header[PI_NET_OFFSET_SIZE]);
|
||
|
pi_dumpdata((char *)data, 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: */
|