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.

656 lines
16 KiB

/*
* $Id: cmp.c,v 1.41 2006/11/07 21:13:24 adridg Exp $
*
* cmp.c: Pilot Connection Management Protocol
*
* Copyright (c) 1996, Kenneth Albanowski.
* Copyright (c) 1999, Tilo Christ
*
* 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.
*
* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include "pi-debug.h"
#include "pi-source.h"
#include "pi-socket.h"
#include "pi-padp.h"
#include "pi-cmp.h"
#include "pi-error.h"
/* Declare prototypes */
static int cmp_flush(pi_socket_t *ps, int flags);
static int cmp_getsockopt(pi_socket_t *ps, int level, int option_name,
void *option_value, size_t *option_len);
static int cmp_setsockopt(pi_socket_t *ps, int level, int option_name,
const void *option_value, size_t *option_len);
static pi_protocol_t *cmp_protocol_dup (pi_protocol_t *prot);
static void cmp_protocol_free (pi_protocol_t *prot);
/* Protocol Functions */
/***********************************************************************
*
* Function: cmp_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*
cmp_protocol_dup (pi_protocol_t *prot)
{
pi_protocol_t *new_prot;
struct pi_cmp_data *data,
*new_data;
new_prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
new_data = (struct pi_cmp_data *)malloc (sizeof (struct pi_cmp_data));
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 = (struct pi_cmp_data *)prot->data;
new_data->type = data->type;
new_data->flags = data->flags;
new_data->version = data->version;
new_data->baudrate = data->baudrate;
new_prot->data = new_data;
} else if (new_prot != NULL) {
free(new_prot);
new_prot = NULL;
} else if (new_data != NULL) {
free(new_data);
new_data = NULL;
}
return new_prot;
}
/***********************************************************************
*
* Function: cmp_protocol_free
*
* Summary: frees an existing pi_protocol struct
*
* Parameters: pi_protocol_t*
*
* Returns: void
*
***********************************************************************/
static void
cmp_protocol_free (pi_protocol_t *prot)
{
if (prot != NULL) {
if (prot->data != NULL)
free(prot->data);
free(prot);
}
}
/***********************************************************************
*
* Function: cmp_protocol
*
* Summary: creates and inits pi_protocol struct instance
*
* Parameters: void
*
* Returns: pi_protocol_t* or NULL if operation failed
*
***********************************************************************/
pi_protocol_t*
cmp_protocol (void)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
data = (struct pi_cmp_data *)malloc (sizeof (struct pi_cmp_data));
if (prot != NULL && data != NULL) {
prot->level = PI_LEVEL_CMP;
prot->dup = cmp_protocol_dup;
prot->free = cmp_protocol_free;
prot->read = cmp_rx;
prot->write = cmp_tx;
prot->flush = cmp_flush;
prot->getsockopt = cmp_getsockopt;
prot->setsockopt = cmp_setsockopt;
data->type = 0;
data->flags = 0;
data->version = 0;
data->baudrate = 0;
prot->data = data;
} else if (prot != NULL) {
free(prot);
prot = NULL;
} else if (data != NULL) {
free(data);
data = NULL;
}
return prot;
}
/***********************************************************************
*
* Function: cmp_rx_handshake
*
* Summary: establishes RX handshake
*
* Parameters: pi_socket_t*, baudrate, hirate enable
*
* Returns: 0 for success, negative otherwise
*
***********************************************************************/
int
cmp_rx_handshake(pi_socket_t *ps, int establishrate,
int establishhighrate)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
pi_buffer_t *buf;
int bytes;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
/* Read the cmp packet */
buf = pi_buffer_new (PI_CMP_HEADER_LEN);
if (buf == NULL) {
errno = ENOMEM;
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
}
bytes = cmp_rx(ps, buf, PI_CMP_HEADER_LEN, 0);
pi_buffer_free (buf);
if (bytes < 0)
return bytes;
if ((data->version & 0xFF00) == 0x0100) {
if (establishrate != -1) {
if (establishrate > data->baudrate) {
if (establishhighrate) {
LOG((PI_DBG_CMP, PI_DBG_LVL_INFO,
"CMP Establishing higher rate %ul (%ul)\n",
establishrate, data->baudrate));
data->baudrate = establishrate;
}
} else {
data->baudrate = establishrate;
}
}
if ((bytes = cmp_init(ps, data->baudrate)) < 0)
return bytes;
} else {
/* 0x80 means the comm version wasn't compatible */
LOG((PI_DBG_CMP, PI_DBG_LVL_ERR, "CMP Incompatible Version\n"));
cmp_abort(ps, 0x80);
errno = ECONNREFUSED;
return pi_set_error(ps->sd, PI_ERR_PROT_INCOMPATIBLE);
}
return 0;
}
/***********************************************************************
*
* Function: cmp_tx_handshake
*
* Summary: establishes TX handshake
*
* Parameters: pi_socket_t*
*
* Returns: >= 0 for success, negative otherwise
*
***********************************************************************/
int
cmp_tx_handshake(pi_socket_t *ps)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
int result;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
if ((result = cmp_wakeup(ps, 38400)) < 0) /* Assume box can't go over 38400 */
return result;
if ((result = cmp_rx(ps, NULL, 0, 0)) < 0)
return result; /* failed to read, errno already set */
switch (data->type) {
case PI_CMP_TYPE_INIT:
return 0;
case PI_CMP_TYPE_ABRT:
LOG((PI_DBG_CMP, PI_DBG_LVL_NONE,
"CMP Aborted by other end\n"));
errno = -EIO;
return pi_set_error(ps->sd, PI_ERR_PROT_ABORTED);
}
return PI_ERR_PROT_INCOMPATIBLE;
}
/***********************************************************************
*
* Function: cmp_tx
*
* Summary: Transmit CMP Packets
*
* Parameters: pi_socket_t*, char* to buf, buf length, flags
*
* Returns: A negative number on error, 0 otherwise
*
***********************************************************************/
ssize_t
cmp_tx(pi_socket_t *ps, const unsigned char *buf, size_t len, int flags)
{
int bytes,
type;
size_t size;
pi_protocol_t *prot,
*next;
struct pi_cmp_data *data;
unsigned char cmp_buf[PI_CMP_HEADER_LEN];
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
next = pi_protocol_next(ps->sd, PI_LEVEL_CMP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
type = padData;
size = sizeof(type);
pi_setsockopt(ps->sd, PI_LEVEL_PADP, PI_PADP_TYPE, &type, &size);
set_byte(&cmp_buf[PI_CMP_OFFSET_TYPE], data->type);
set_byte(&cmp_buf[PI_CMP_OFFSET_FLGS], data->flags);
set_short(&cmp_buf[PI_CMP_OFFSET_VERS], data->version > PI_CMP_VERSION ? PI_CMP_VERSION : data->version);
set_short(&cmp_buf[PI_CMP_OFFSET_RESV], 0);
set_long(&cmp_buf[PI_CMP_OFFSET_BAUD], data->baudrate);
CHECK(PI_DBG_CMP, PI_DBG_LVL_INFO, cmp_dump(cmp_buf, 1));
bytes = next->write(ps, cmp_buf, PI_CMP_HEADER_LEN, flags);
if (bytes < 10)
return (bytes < 0) ? bytes : pi_set_error(ps->sd, PI_ERR_PROT_ABORTED);
return 0;
}
/***********************************************************************
*
* Function: cmp_rx
*
* Summary: Receive CMP packets
*
* Parameters: None
*
* Returns: A negative number on error, 0 otherwise
*
***********************************************************************/
ssize_t
cmp_rx(pi_socket_t *ps, pi_buffer_t *msg, size_t len, int flags)
{
int bytes;
pi_protocol_t *prot,
*next;
struct pi_cmp_data *data;
LOG((PI_DBG_CMP, PI_DBG_LVL_DEBUG, "CMP RX len=%d flags=0x%02x\n",
len, flags));
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
next = pi_protocol_next(ps->sd, PI_LEVEL_CMP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
bytes = next->read(ps, msg, len, flags);
if (bytes < 10)
return pi_set_error(ps->sd, (bytes < 0) ? bytes : PI_ERR_PROT_ABORTED);
CHECK(PI_DBG_CMP, PI_DBG_LVL_INFO, cmp_dump(msg->data, 0));
data->type = get_byte(&msg->data[PI_CMP_OFFSET_TYPE]);
data->flags = get_byte(&msg->data[PI_CMP_OFFSET_FLGS]);
data->version = get_short(&msg->data[PI_CMP_OFFSET_VERS]);
data->baudrate = get_long(&msg->data[PI_CMP_OFFSET_BAUD]);
return 0;
}
/***********************************************************************
*
* Function: cmp_flush
*
* Summary: Flush input and output buffers
*
* Parameters: pi_socket_t*, flags
*
* Returns: A negative number on error, 0 otherwise
*
***********************************************************************/
static int
cmp_flush(pi_socket_t *ps, int flags)
{
pi_protocol_t *prot,
*next;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
next = pi_protocol_next(ps->sd, PI_LEVEL_CMP);
if (next == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
return next->flush(ps, flags);
}
/***********************************************************************
*
* Function: cmp_init
*
* Summary: Initialize the socket for CMP transmission
*
* Parameters: pi_socket_t*, baudrate
*
* Returns: Number of packets transmitted
*
***********************************************************************/
int
cmp_init(pi_socket_t *ps, int baudrate)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
data->type = PI_CMP_TYPE_INIT;
data->flags = CMP_FL_LONG_PACKET_SUPPORT;
if (baudrate != 9600)
data->flags = CMP_FL_CHANGE_BAUD_RATE;
data->baudrate = baudrate;
return cmp_tx(ps, NULL, 0, 0);
}
/***********************************************************************
*
* Function: cmp_abort
*
* Summary: Abort a CMP session in progress
*
* Parameters: pi_socket_t*
*
* Returns: Number of PADP packets transmitted or negative on error
*
***********************************************************************/
int
cmp_abort(pi_socket_t *ps, int reason)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
data->type = PI_CMP_TYPE_ABRT;
data->flags = reason;
LOG((PI_DBG_CMP, PI_DBG_LVL_NONE, "CMP ABORT\n"));
return cmp_tx (ps, NULL, 0, 0);
}
/***********************************************************************
*
* Function: cmp_wakeup
*
* Summary: Wakeup the CMP listener process
*
* Parameters: pi_socket_t*
*
* Returns: Number of PADP packets transmitted or negative on error
*
***********************************************************************/
int
cmp_wakeup(pi_socket_t *ps, int maxbaud)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
data->type = PI_CMP_TYPE_WAKE;
data->flags = 0;
data->version = PI_CMP_VERSION;
data->baudrate = maxbaud;
return cmp_tx(ps, NULL, 0, 0);
}
/***********************************************************************
*
* Function: cmp_getsockopt
*
* Summary: get options on socket
*
* Parameters: pi_socket*, level, option name, option value, option length
*
* Returns: 0 for success, negative otherwise
*
***********************************************************************/
static int
cmp_getsockopt(pi_socket_t *ps, int level, int option_name,
void *option_value, size_t *option_len)
{
pi_protocol_t *prot;
struct pi_cmp_data *data;
(void) level;
prot = pi_protocol(ps->sd, PI_LEVEL_CMP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_cmp_data *)prot->data;
switch (option_name) {
case PI_CMP_TYPE:
if (*option_len != sizeof (data->type))
goto error;
memcpy (option_value, &data->type,
sizeof (data->type));
*option_len = sizeof (data->type);
break;
case PI_CMP_FLAGS:
if (*option_len != sizeof (data->flags))
goto error;
memcpy (option_value, &data->flags,
sizeof (data->flags));
*option_len = sizeof (data->flags);
break;
case PI_CMP_VERS:
if (*option_len != sizeof (data->version))
goto error;
memcpy (option_value, &data->version,
sizeof (data->version));
*option_len = sizeof (data->version);
break;
case PI_CMP_BAUD:
if (*option_len != sizeof (data->baudrate))
goto error;
memcpy (option_value, &data->baudrate,
sizeof (data->baudrate));
*option_len = sizeof (data->baudrate);
break;
}
return 0;
error:
errno = EINVAL;
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
}
/***********************************************************************
*
* Function: cmp_setsockopt
*
* Summary: set options on socket
*
* Parameters: pi_socket*, level, option name, option value, option length
*
* Returns: 0 for success, negative otherwise
*
***********************************************************************/
static int
cmp_setsockopt(pi_socket_t *ps, int level, int option_name,
const void *option_value, size_t *option_len)
{
pi_protocol_t *prot;
struct pi_padp_data *data;
(void) level;
prot = pi_protocol(ps->sd, PI_LEVEL_PADP);
if (prot == NULL)
return pi_set_error(ps->sd, PI_ERR_SOCK_INVALID);
data = (struct pi_padp_data *)prot->data;
if (option_name == PI_PADP_TYPE) {
if (*option_len != sizeof (data->type))
goto error;
memcpy (&data->type, option_value,
sizeof (data->type));
*option_len = sizeof (data->type);
}
return 0;
error:
errno = EINVAL;
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
}
/***********************************************************************
*
* Function: cmp_dump
*
* Summary: Dump the CMP packet frames
*
* Parameters: char* to cmp packet, TX boolean
*
* Returns: void
*
***********************************************************************/
void
cmp_dump(const unsigned char *cmp, int rxtx)
{
char *type;
(void) rxtx;
switch (get_byte(&cmp[PI_CMP_OFFSET_TYPE])) {
case PI_CMP_TYPE_WAKE:
type = "WAKE";
break;
case PI_CMP_TYPE_INIT:
type = "INIT";
break;
case PI_CMP_TYPE_ABRT:
type = "ABRT";
break;
default:
type = "UNK";
break;
}
LOG((PI_DBG_CMP, PI_DBG_LVL_NONE,
"CMP %s %s Type: 0x%02x Flags: 0x%02x Version: 0x%04x Baud: %d\n",
rxtx ? "TX" : "RX", type,
get_byte(&cmp[PI_CMP_OFFSET_TYPE]),
get_byte(&cmp[PI_CMP_OFFSET_FLGS]),
get_long(&cmp[PI_CMP_OFFSET_VERS]),
get_long(&cmp[PI_CMP_OFFSET_BAUD])));
}
/* 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: */