/* packet-gryphon.c
 * Routines for Gryphon protocol packet disassembly
 *
 * $Id: packet-gryphon.c,v 1.23 2001/07/08 21:51:32 hagbard Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Steve Limkemann <stevelim@dgtech.com>
 * Copyright 1998 Steve Limkemann
 *
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "plugins/plugin_api.h"

#include "moduleinfo.h"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <string.h>
#include <ctype.h>
#include <time.h>

#include <gmodule.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include "packet.h"
#include "packet-gryphon.h"

#ifndef __ETHEREAL_STATIC__
G_MODULE_EXPORT const gchar version[] = VERSION;
#endif

#ifndef G_HAVE_GINT64
#error "Sorry, this won't compile without 64-bit integer support"
#endif                                                                  

/*
 * See
 *
 *	http://www.dgtech.com/gryphon/docs/html/
 */

static int proto_gryphon = -1;

static int hf_gryph_src = -1;
static int hf_gryph_srcchan = -1;
static int hf_gryph_dest = -1;
static int hf_gryph_destchan= -1;
static int hf_gryph_type = -1;
static int hf_gryph_cmd = -1;

static gint ett_gryphon = -1;
static gint ett_gryphon_header = -1;
static gint ett_gryphon_body = -1;
static gint ett_gryphon_command_data = -1;
static gint ett_gryphon_response_data = -1;
static gint ett_gryphon_data_header = -1;
static gint ett_gryphon_flags = -1;
static gint ett_gryphon_data_body = -1;
static gint ett_gryphon_cmd_filter_block = -1;
static gint ett_gryphon_cmd_events_data = -1;
static gint ett_gryphon_cmd_config_device = -1;
static gint ett_gryphon_cmd_sched_data = -1;
static gint ett_gryphon_cmd_sched_cmd = -1;
static gint ett_gryphon_cmd_response_block = -1;
static gint ett_gryphon_pgm_list = -1;
static gint ett_gryphon_pgm_status = -1;
static gint ett_gryphon_pgm_options = -1;

static int dissect_gryphon_message(tvbuff_t *tvb, int offset,
    proto_tree *tree, gboolean is_msgresp_add);
static int decode_command(tvbuff_t*, int, int, int, proto_tree*);
static int decode_response(tvbuff_t*, int, int, int, proto_tree*);
static int decode_data(tvbuff_t*, int, int, int, proto_tree*);
static int decode_event(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_init(tvbuff_t*, int, int, int, proto_tree*);
static int resp_time(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_setfilt(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_ioctl(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_addfilt(tvbuff_t*, int, int, int, proto_tree*);
static int resp_addfilt(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_modfilt(tvbuff_t*, int, int, int, proto_tree*);
static int resp_filthan(tvbuff_t*, int, int, int, proto_tree*);
static int dfiltmode(tvbuff_t*, int, int, int, proto_tree*);
static int filtmode(tvbuff_t*, int, int, int, proto_tree*);
static int resp_events(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_register(tvbuff_t*, int, int, int, proto_tree*);
static int resp_register(tvbuff_t*, int, int, int, proto_tree*);
static int resp_getspeeds(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_sort(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_optimize(tvbuff_t*, int, int, int, proto_tree*);
static int resp_config(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_sched(tvbuff_t*, int, int, int, proto_tree*);
static int resp_blm_data(tvbuff_t*, int, int, int, proto_tree*);
static int resp_blm_stat(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_addresp(tvbuff_t*, int, int, int, proto_tree*);
static int resp_addresp(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_modresp(tvbuff_t*, int, int, int, proto_tree*);
static int resp_resphan(tvbuff_t*, int, int, int, proto_tree*);
static int resp_sched(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_desc(tvbuff_t*, int, int, int, proto_tree*);
static int resp_desc(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_upload(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_delete(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_list(tvbuff_t*, int, int, int, proto_tree*);
static int resp_list(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_start(tvbuff_t*, int, int, int, proto_tree*);
static int resp_start(tvbuff_t*, int, int, int, proto_tree*);
static int resp_status(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_options(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_files(tvbuff_t*, int, int, int, proto_tree*);
static int resp_files(tvbuff_t*, int, int, int, proto_tree*);
static int eventnum(tvbuff_t*, int, int, int, proto_tree*);
static int speed(tvbuff_t*, int, int, int, proto_tree*);
static int filter_block(tvbuff_t*, int, int, int, proto_tree*);
static int blm_mode(tvbuff_t*, int, int, int, proto_tree*);
static int cmd_usdt(tvbuff_t*, int, int, int, proto_tree*);

static char *frame_type[] = {
	"",
	"Command request",
	"Command response",
	"Network (vehicle) data",
	"Event",
	"Miscellaneous",
	"Text string"
};

static void
dissect_gryphon(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    int offset;
    proto_item *ti;
    proto_tree *gryphon_tree;
    guint8 frmtyp;

    if (check_col(pinfo->fd, COL_PROTOCOL))
	col_set_str(pinfo->fd, COL_PROTOCOL, "Gryphon");
    if (check_col(pinfo->fd, COL_INFO))
	col_clear(pinfo->fd, COL_INFO);

    ti = proto_tree_add_item(tree, proto_gryphon, tvb, 0,
	    	    tvb_length(tvb), FALSE);
    gryphon_tree = proto_item_add_subtree(ti, ett_gryphon);

    if (check_col(pinfo->fd, COL_INFO)) {
	/*
	 * Indicate what kind of message the first message is.
	 */
	frmtyp = tvb_get_guint8(tvb, 6) & ~RESPONSE_FLAGS;
	if (frmtyp >= SIZEOF (frame_type))
    	    col_set_str(pinfo->fd, COL_INFO, "- Invalid -");
	else
    	    col_set_str(pinfo->fd, COL_INFO, frame_type[frmtyp]);
    }

    if (tree) {
	offset = 0;
	while (tvb_reported_length_remaining(tvb, offset) > 0)
	    offset = dissect_gryphon_message(tvb, offset, gryphon_tree, FALSE);
    }
}

static int
dissect_gryphon_message(tvbuff_t *tvb, int offset, proto_tree *tree,
    gboolean is_msgresp_add)
{
    proto_tree	    *header_tree, *body_tree, *localTree;
    proto_item	    *header_item, *body_item, *localItem;
    int		    start_offset, msgend;
    int		    msglen, msgpad;
    unsigned int    src, dest, i, frmtyp;
    guint8	    flags;
    static const value_string src_dest[] = {
	    {SD_CARD,   	"Card"},
	    {SD_SERVER,     	"Server"},
	    {SD_CLIENT,		"Client"},
	    {SD_SCHED,		"Scheduler"},
	    {SD_SCRIPT,		"Script Processor"},
	    {SD_PGM,     	"Program Loader"},
	    {SD_USDT,     	"USDT Server"},
	    {SD_BLM,	    	"Bus Load Monitoring"},
	    {SD_FLIGHT,   	"Flight Recorder"},
	    {SD_RESP,     	"Message Responder"},
	    {-1,		"- unknown -"},
	    };

    src = tvb_get_guint8(tvb, offset + 0);
    dest = tvb_get_guint8(tvb, offset + 2);
    msglen = tvb_get_ntohs(tvb, offset + 4);
    flags = tvb_get_guint8(tvb, offset + 6);
    frmtyp = flags & ~RESPONSE_FLAGS;
    if (frmtyp >= SIZEOF (frame_type)) {
	/*
	 * Unknown message type.
	 */
	proto_tree_add_text(tree, tvb, offset, msglen, "Data");
	offset += msglen;
	return offset;
    }

    header_item = proto_tree_add_text(tree, tvb, offset, MSG_HDR_SZ, "Header");
    header_tree = proto_item_add_subtree(header_item, ett_gryphon_header);
    for (i = 0; i < SIZEOF(src_dest); i++) {
	if (src_dest[i].value == src)
	    break;
    }
    if (i >= SIZEOF(src_dest))
	i = SIZEOF(src_dest) - 1;
    proto_tree_add_text(header_tree, tvb, offset, 2,
	"Source: %s, channel %u", src_dest[i].strptr,
	tvb_get_guint8(tvb, offset + 1));
    proto_tree_add_uint_hidden(header_tree, hf_gryph_src, tvb,
	offset, 1, src);
    proto_tree_add_uint_hidden(header_tree, hf_gryph_srcchan, tvb,
	offset+1, 1, tvb_get_guint8(tvb, offset + 1));

    for (i = 0; i < SIZEOF(src_dest); i++) {
	if (src_dest[i].value == dest)
	    break;
    }
    if (i >= SIZEOF(src_dest))
	i = SIZEOF(src_dest) - 1;
    proto_tree_add_text(header_tree, tvb, offset+2, 2,
	"Destination: %s, channel %u", src_dest[i].strptr,
	tvb_get_guint8(tvb, offset + 3));
    proto_tree_add_uint_hidden(header_tree, hf_gryph_dest, tvb,
	offset+2, 1, dest);
    proto_tree_add_uint_hidden(header_tree, hf_gryph_destchan, tvb,
	offset+3, 1, tvb_get_guint8(tvb, offset + 3));

    proto_tree_add_text(header_tree, tvb, offset+4, 2,
	"Data length: %u bytes", msglen);
    proto_tree_add_text(header_tree, tvb, offset+6, 1,
	"Frame type: %s", frame_type[frmtyp]);
    if (is_msgresp_add) {
	localItem = proto_tree_add_text(header_tree, tvb, offset+6, 1, "Flags");
	localTree = proto_item_add_subtree (localItem, ett_gryphon_flags);
	if (flags & DONT_WAIT_FOR_RESP) {
	    proto_tree_add_text(localTree, tvb, offset+6, 1,
		    	    "1... .... = Don't wait for response");
	} else {
	    proto_tree_add_text(localTree, tvb, offset+6, 1,
		    	    "0... .... = Wait for response");
	}
	if (flags & WAIT_FOR_PREV_RESP) {
	    proto_tree_add_text(localTree, tvb, offset+6, 1,
		    	    ".1.. .... = Wait for previous responses");
	} else {
	    proto_tree_add_text(localTree, tvb, offset+6, 1,
		    	    ".0.. .... = Don't wait for previous responses");
	}
    }
    proto_tree_add_text(header_tree, tvb, offset+7, 1, "reserved");

    proto_tree_add_uint_hidden(header_tree, hf_gryph_type, tvb,
	offset+6, 1, frmtyp);
    msgpad = 3 - (msglen + 3) % 4;
    msgend = offset + msglen + msgpad + MSG_HDR_SZ;

    body_item = proto_tree_add_text(tree, tvb, offset + MSG_HDR_SZ,
	msglen + msgpad, "Body");
    body_tree = proto_item_add_subtree(body_item, ett_gryphon_body);

    start_offset = offset;
    offset += MSG_HDR_SZ;
    switch (frmtyp) {
    case GY_FT_CMD:
	offset = decode_command(tvb, offset, dest, msglen, body_tree);
	break;
    case GY_FT_RESP:
	offset = decode_response(tvb, offset, src, msglen, body_tree);
	break;
    case GY_FT_DATA:
	offset = decode_data(tvb, offset, src, msglen, body_tree);
	break;
    case GY_FT_EVENT:
	offset = decode_event(tvb, offset, src, msglen, body_tree);
	break;
    case GY_FT_MISC:
	break;
    case GY_FT_TEXT:
	break;
    default:
	break;
    }
    if (offset < msgend - msgpad) {
	i = msgend - msgpad - offset;
	proto_tree_add_text(tree, tvb, offset, i, "Data");
	offset += i;
    }
    if (offset < msgend) {
	i = msgend - offset;
	proto_tree_add_text(tree, tvb, offset, i, "padding");
	offset += i;
    }
    return offset;
}


static const val_str_dsp cmds[] = {
    	{CMD_INIT,	    	"Initialize", cmd_init, NULL},
        {CMD_GET_STAT,  	"Get status", NULL, NULL},
        {CMD_GET_CONFIG,	"Get configuration", NULL, resp_config},
        {CMD_EVENT_ENABLE,  	"Enable event", eventnum, NULL},
        {CMD_EVENT_DISABLE, 	"Disable event", eventnum, NULL},
	{CMD_GET_TIME,  	"Get time", NULL, resp_time},
	{CMD_GET_RXDROP,	"Get number of dropped RX messages", NULL, NULL},
	{CMD_RESET_RXDROP,  	"Clear number of dropped RX messages", NULL, NULL},
	{CMD_BCAST_ON,  	"Set broadcasts on", NULL, NULL},
	{CMD_BCAST_OFF, 	"Set broadcasts off", NULL, NULL},
	{CMD_CARD_SET_SPEED, 	"Set channel baud rate", speed, NULL},
	{CMD_CARD_GET_SPEED, 	"Get channel baud rate", NULL, speed},
	{CMD_CARD_SET_FILTER, 	"Set filter (deprecated)", cmd_setfilt, NULL},
	{CMD_CARD_GET_FILTER, 	"Get filter", resp_addfilt, cmd_addfilt},
	{CMD_CARD_TX,    	"Transmit message", decode_data, NULL},
	{CMD_CARD_TX_LOOP_ON, 	"Set transmit loopback on", NULL, NULL},
	{CMD_CARD_TX_LOOP_OFF,  "Set transmit loopback off", NULL, NULL},
	{CMD_CARD_IOCTL, 	"IOCTL pass-through", cmd_ioctl, NULL},
	{CMD_CARD_ADD_FILTER, 	"Add a filter", cmd_addfilt, resp_addfilt},
	{CMD_CARD_MODIFY_FILTER, "Modify a filter", cmd_modfilt, NULL},
	{CMD_CARD_GET_FILTER_HANDLES, "Get filter handles", NULL, resp_filthan},
	{CMD_CARD_SET_DEFAULT_FILTER, "Set default filter", dfiltmode, NULL},
	{CMD_CARD_GET_DEFAULT_FILTER, "Get default filter mode", NULL, dfiltmode},
	{CMD_CARD_SET_FILTER_MODE, "Set filter mode", filtmode, NULL},
	{CMD_CARD_GET_FILTER_MODE, "Get filter mode", NULL, filtmode},
	{CMD_CARD_GET_EVNAMES,	"Get event names", NULL, resp_events},
	{CMD_CARD_GET_SPEEDS,	"Get defined speeds", NULL, resp_getspeeds},
	{CMD_SERVER_REG, 	"Register with server", cmd_register, resp_register},
    	{CMD_SERVER_SET_SORT,	"Set the sorting behavior", cmd_sort, NULL},
    	{CMD_SERVER_SET_OPT,  	"Set the type of optimization", cmd_optimize, NULL},
	{CMD_BLM_SET_MODE,	"Set Bus Load Monitoring mode", blm_mode, NULL},
	{CMD_BLM_GET_MODE,	"Get Bus Load Monitoring mode", NULL, blm_mode},
	{CMD_BLM_GET_DATA,	"Get Bus Load data", NULL, resp_blm_data},
	{CMD_BLM_GET_STATS,	"Get Bus Load statistics", NULL, resp_blm_stat},
	{CMD_FLIGHT_GET_CONFIG, "Get flight recorder channel info", NULL, NULL},
	{CMD_FLIGHT_START_MON,  "Start flight recorder monitoring", NULL, NULL},
	{CMD_FLIGHT_STOP_MON, 	"Stop flight recorder monitoring", NULL, NULL},
	{CMD_MSGRESP_ADD,	"Add response message", cmd_addresp, resp_addresp},
	{CMD_MSGRESP_GET,	"Get response message", resp_addresp, cmd_addresp},
	{CMD_MSGRESP_MODIFY, 	"Modify response message state", cmd_modresp, NULL},
	{CMD_MSGRESP_GET_HANDLES, "Get response message handles", NULL, resp_resphan},
	{CMD_PGM_DESC,   	"Describe program to to uploaded", cmd_desc, resp_desc},
	{CMD_PGM_UPLOAD, 	"Upload a program to the Gryphon", cmd_upload, NULL},
	{CMD_PGM_DELETE, 	"Delete an uploaded program", cmd_delete, NULL},
	{CMD_PGM_LIST,   	"Get a list of uploaded programs", cmd_list, resp_list},
	{CMD_PGM_START,  	"Start an uploaded program", cmd_start, resp_start},
	{CMD_PGM_STOP,   	"Stop an uploaded program", resp_start, NULL},
	{CMD_PGM_STATUS, 	"Get status of an uploaded program", cmd_delete, resp_status},
	{CMD_PGM_OPTIONS, 	"Set program upload options", cmd_options, resp_status},
    	{CMD_PGM_FILES,     	"Get a list of files & directories", cmd_files, resp_files},
	{CMD_SCHED_TX,   	"Schedule transmission of messages", cmd_sched, resp_sched},
	{CMD_SCHED_KILL_TX,	"Stop and destroy a message transmission", NULL, NULL},
	{CMD_SCHED_STOP_TX,	"Kill a message transmission (deprecated)", NULL, NULL},
	{CMD_USDT_IOCTL,    	"Register/Unregister with USDT server", cmd_usdt, NULL},
	{-1,	    	    	"- unknown -", NULL, NULL},
        };

static const value_string responses[] = {
	{RESP_OK,		"OK - no error"},
	{RESP_UNKNOWN_ERR,	"Unknown error"},
	{RESP_UNKNOWN_CMD,	"Unrecognised command"},
	{RESP_UNSUPPORTED,	"Unsupported command"},
	{RESP_INVAL_CHAN,	"Invalid channel specified"},
	{RESP_INVAL_DST,	"Invalid destination"},
	{RESP_INVAL_PARAM,	"Invalid parameter(s)"},
	{RESP_INVAL_MSG,	"Invalid message"},
	{RESP_INVAL_LEN,	"Invalid length field"},
	{RESP_TX_FAIL,		"Transmit failed"},
	{RESP_RX_FAIL,		"Receive failed"},
	{RESP_AUTH_FAIL,	"Authorization failed"},
	{RESP_MEM_ALLOC_ERR,  	"Memory allocation error"},
	{RESP_TIMEOUT,	  	"Command timed out"},
	{RESP_UNAVAILABLE,	"Unavailable"},
	{RESP_BUF_FULL,		"Buffer full"},
	{RESP_NO_SUCH_JOB,	"No such job"},
	{-1,	    	    	"- unknown -"},
	};
	
static const value_string filter_data_types[] = {
	{FILTER_DATA_TYPE_HEADER_FRAME, "frame header"},
	{FILTER_DATA_TYPE_HEADER,   	"data message header"},
	{FILTER_DATA_TYPE_DATA,     	"data message data"},
	{FILTER_DATA_TYPE_EXTRA_DATA,	"data message extra data"},
	{FILTER_EVENT_TYPE_HEADER,  	"event message header"},
	{FILTER_EVENT_TYPE_DATA,    	"event message"},
	{-1,	    	    	    	"- unknown -"},
	};

static const value_string operators[] = {
	{BIT_FIELD_CHECK,    	"Bit field check"},
	{SVALUE_GT,  	    	"Greater than (signed)"},
	{SVALUE_GE,  	    	"Greater than or equal to (signed)"},
	{SVALUE_LT,  	    	"Less than (signed)"},
	{SVALUE_LE,  	    	"Less than or equal to (signed)"},
	{VALUE_EQ,   	    	"Equal to"},
	{VALUE_NE,   	    	"Not equal to"},
	{UVALUE_GT,  	    	"Greater than (unsigned)"},
	{UVALUE_GE,  	    	"Greater than or equal to (unsigned)"},
	{UVALUE_LT,  	    	"Less than (unsigned)"},
	{UVALUE_LE,  	    	"Less than or equal to (unsigned)"},
	{DIG_LOW_TO_HIGH,    	"Digital, low to high transistion"},
	{DIG_HIGH_TO_LOW,    	"Digital, high to low transistion"},
	{DIG_TRANSITION,     	"Digital, change of state"},
	{-1,	    	    	"- unknown -"},
	};

static const value_string modes[] = {
	{FILTER_OFF_PASS_ALL,	"Filter off, pass all messages"},
	{FILTER_OFF_BLOCK_ALL,	"Filter off, block all messages"},
	{FILTER_ON, 	    	"Filter on"},
	{-1,	    	    	"- unknown -"},
	};

static const value_string dmodes[] = {
	{DEFAULT_FILTER_BLOCK,	"Block"},
	{DEFAULT_FILTER_PASS,	"Pass"},
	{-1,	    	    	"- unknown -"},
	};

static const value_string filtacts[] = {
	{DELETE_FILTER,     	"Delete"},
	{ACTIVATE_FILTER,   	"Activate"},
	{DEACTIVATE_FILTER, 	"Deactivate"},
	{-1,	    	    	"- unknown -"},
	};

static const value_string ioctls[] = {
	{GINIT,     	    	"GINIT: Initialize"},
	{GLOOPON,   	    	"GLOOPON: Loop on"},
	{GLOOPOFF,  	    	"GLOOPOFF: Loop off"},
	{GGETHWTYPE,	    	"GGETHWTYPE: Get hardware type"},
	{GGETREG,   	    	"GGETREG: Get register"},
	{GSETREG,   	    	"GSETREG: Set register"},
	{GGETRXCOUNT,	    	"GGETRXCOUNT: Get the receive message counter"},
	{GSETRXCOUNT,	    	"GSETRXCOUNT: Set the receive message counter"},
	{GGETTXCOUNT,	    	"GGETTXCOUNT: Get the transmit message counter"},
	{GSETTXCOUNT,	    	"GSETTXCOUNT: Set the transmit message counter"},
	{GGETRXDROP, 	    	"GGETRXDROP: Get the number of dropped receive messages"},
	{GSETRXDROP,	    	"GSETRXDROP: Set the number of dropped receive messages"},
	{GGETTXDROP,	    	"GGETTXDROP: Get the number of dropped transmit messages"},
	{GSETTXDROP,	    	"GSETTXDROP: Set the number of dropped transmit messages"},
	{GGETRXBAD, 	    	"GGETRXBAD: Get the number of bad receive messages"},
	{GGETTXBAD, 	    	"GGETTXBAD: Get the number of bad transmit messages"},
	{GGETCOUNTS,	    	"GGETCOUNTS: Get total message counter"},
	{GGETBLMON, 	    	"GGETBLMON: Get bus load monitoring status"},
	{GSETBLMON, 	    	"GSETBLMON: Set bus load monitoring status (turn on/off)"},
	{GGETERRLEV,	    	"GGETERRLEV: Get error level"},
	{GSETERRLEV,	    	"GSETERRLEV: Set error level"},
	{GGETBITRATE,	    	"GGETBITRATE: Get bit rate"},
	{GGETRAM,   	    	"GGETRAM: Read value from RAM"},
	{GSETRAM,   	    	"GSETRAM: Write value to RAM"},
	{GCANGETBTRS,	    	"GCANGETBTRS: Read CAN bit timing registers"},
	{GCANSETBTRS,	    	"GCANSETBTRS: Write CAN bit timing registers"},
	{GCANGETBC, 	    	"GCANGETBC: Read CAN byte count"},
	{GCANSETBC, 	    	"GCANSETBC: Write CAN byte count"},
	{GCANGETMODE,	    	"GCANGETMODE"},
	{GCANSETMODE,	    	"GCANSETMODE"},
	{GCANGETTRANS,	    	"GCANGETTRANS"},
	{GCANSETTRANS,	    	"GCANSETTRANS"},
	{GCANSENDERR,	    	"GCANSENDERR"},
	{GCANRGETOBJ,	    	"GCANRGETOBJ"},
	{GCANRSETSTDID,     	"GCANRSETSTDID"},
	{GCANRSETEXTID,     	"GCANRSETEXTID"},
	{GCANRSETDATA,	    	"GCANRSETDATA"},
	{GCANRENABLE,	    	"GCANRENABLE"},
	{GCANRDISABLE,	    	"GCANRDISABLE"},
	{GCANRGETMASKS,     	"GCANRGETMASKS"},
	{GCANRSETMASKS,     	"GCANRSETMASKS"},
	{GCANSWGETMODE,     	"GCANSWGETMODE"},
	{GCANSWSETMODE,     	"GCANSWSETMODE"},
	{GDLCGETFOURX,	    	"GDLCGETFOURX"},
	{GDLCSETFOURX,	    	"GDLCSETFOURX"},
	{GDLCGETLOAD,	    	"GDLCGETLOAD"},
	{GDLCSETLOAD,	    	"GDLCSETLOAD"},
	{GDLCSENDBREAK,     	"GDLCSENDBREAK"},
	{GDLCABORTTX,	    	"GDLCABORTTX"},
	{GDLCGETHDRMODE,     	"DLCGETHDRMODE"},
	{GDLCSETHDRMODE,    	"GDLCSETHDRMODE"},
	{GHONSLEEP, 	    	"GHONSLEEP"},
	{GHONSILENCE,	    	"GHONSILENCE"},
	{GKWPSETPTIMES,     	"GKWPSETPTIMES"},
	{GKWPSETWTIMES,     	"GKWPSETWTIMES"},
	{GKWPDOWAKEUP,	    	"GKWPDOWAKEUP"},
	{GKWPGETBITTIME,    	"GKWPGETBITTIME"},
	{GKWPSETBITTIME,    	"GKWPSETBITTIME"},
	{GKWPSETNODEADDR,   	"GKWPSETNODEADDR"},
	{GKWPGETNODETYPE,   	"GKWPGETNODETYPE"},
	{GKWPSETNODETYPE,   	"GKWPSETNODETYPE"},
	{GKWPSETWAKETYPE,   	"GKWPSETWAKETYPE"},
	{GKWPSETTARGADDR,   	"GKWPSETTARGADDR"},
	{GKWPSETKEYBYTES,   	"GKWPSETKEYBYTES"},
	{GKWPSETSTARTREQ,   	"GKWPSETSTARTREQ"},
	{GKWPSETSTARTRESP,  	"GKWPSETSTARTRESP"},
	{GKWPSETPROTOCOL,   	"GKWPSETPROTOCOL"},
	{GKWPGETLASTKEYBYTES,	"GKWPGETLASTKEYBYTES"},
	{GKWPSETLASTKEYBYTES,	"GKWPSETLASTKEYBYTES"},
	{GSCPGETBBR,	    	"GSCPGETBBR"},
	{GSCPSETBBR, 	    	"GSCPSETBBR"},
	{GSCPGETID, 	    	"GSCPGETID"},
	{GSCPSETID, 	    	"GSCPSETID"},
	{GSCPADDFUNCID,     	"GSCPADDFUNCID"},
	{GSCPCLRFUNCID,     	"GSCPCLRFUNCID"},
	{GUBPGETBITRATE,    	"GUBPGETBITRATE"},
	{GUBPSETBITRATE,    	"GUBPSETBITRATE"},
	{GUBPGETINTERBYTE,  	"GUBPGETINTERBYTE"},
	{GUBPSETINTERBYTE,  	"GUBPSETINTERBYTE"},
	{GUBPGETNACKMODE,   	"GUBPGETNACKMODE"},
	{GUBPSETNACKMODE,   	"GUBPSETNACKMODE"},
	{-1, 	    	    	"- unknown -"},
	};


static int
decode_command(tvbuff_t *tvb, int offset, int dst, int msglen, proto_tree *pt)
{
    int     	    cmd, padding;
    unsigned int    i;
    proto_tree	    *ft;
    proto_item	    *ti;

    cmd = tvb_get_guint8(tvb, offset);
    proto_tree_add_uint_hidden(pt, hf_gryph_cmd, tvb, offset, 1, cmd);
    if (cmd > 0x3F)
    	cmd += dst * 256;

    for (i = 0; i < SIZEOF(cmds); i++) {
    	if (cmds[i].value == cmd)
	    break;
    }
    if (i >= SIZEOF(cmds) && dst >= SD_KNOWN) {
    	cmd = (cmd & 0xFF) + SD_CARD * 256;
	for (i = 0; i < SIZEOF(cmds); i++) {
    	    if (cmds[i].value == cmd)
		break;
	}
    }
    if (i >= SIZEOF(cmds))
    	i = SIZEOF(cmds) - 1;

    proto_tree_add_text (pt, tvb, offset, 4, "Command: %s", cmds[i].strptr);
    offset += 4;
    msglen -= 4;

    if (cmds[i].cmd_fnct && msglen > 0) {
    	padding = 3 - (msglen + 3) % 4;
 	ti = proto_tree_add_text(pt, tvb, offset, msglen, "Data: (%d bytes)", msglen);
	ft = proto_item_add_subtree(ti, ett_gryphon_command_data);
	offset = (*(cmds[i].cmd_fnct)) (tvb, offset, dst, msglen, ft);
    }
    return offset;
}

static int
decode_response(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int     	    cmd;
    unsigned int    i, j, resp;
    proto_tree	    *ft;
    proto_item	    *ti;

    cmd = tvb_get_guint8(tvb, offset);
    if (cmd > 0x3F)
    	cmd += src * 256;

    for (i = 0; i < SIZEOF(cmds); i++) {
    	if (cmds[i].value == cmd)
	    break;
    }
    if (i >= SIZEOF(cmds) && src >= SD_KNOWN) {
    	cmd = (cmd & 0xFF) + SD_CARD * 256;
	for (i = 0; i < SIZEOF(cmds); i++) {
    	    if (cmds[i].value == cmd)
		break;
	}
    }
    if (i >= SIZEOF(cmds))
    	i = SIZEOF(cmds) - 1;
    proto_tree_add_text (pt, tvb, offset, 4, "Command: %s", cmds[i].strptr);
    offset += 4;
    msglen -= 4;
    
    resp = tvb_get_ntohl (tvb, offset);
    for (j = 0; j < SIZEOF(responses); j++) {
    	if (responses[j].value == resp)
	    break;
    }
    if (j >= SIZEOF(responses))
    	j = SIZEOF(responses) - 1;
    proto_tree_add_text (pt, tvb, offset, 4, "Status: %s", responses[j].strptr);
    offset += 4;
    msglen -= 4;

    if (cmds[i].rsp_fnct && msglen > 0) {
	ti = proto_tree_add_text(pt, tvb, offset, msglen, "Data: (%d bytes)", msglen);
	ft = proto_item_add_subtree(ti, ett_gryphon_response_data);
	offset = (*(cmds[i].rsp_fnct)) (tvb, offset, src, msglen, ft);
    }
    return offset;
}

static int
decode_data(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item, *item1;
    proto_tree	*tree, *tree1;
    int     hdrsize, datasize, extrasize, hdrbits, msgsize, padding, mode;
    int     hours, minutes, seconds, fraction;
    unsigned long   timestamp;

    hdrsize = tvb_get_guint8(tvb, offset+0);
    hdrbits = tvb_get_guint8(tvb, offset+1);
    datasize = tvb_get_ntohs(tvb, offset+2);
    extrasize = tvb_get_guint8(tvb, offset+4);
    padding = 3 - (hdrsize + datasize + extrasize + 3) % 4;
    msgsize = hdrsize + datasize + extrasize + padding + 16;

    item = proto_tree_add_text(pt, tvb, offset, 16, "Message header");
    tree = proto_item_add_subtree (item, ett_gryphon_data_header);
    proto_tree_add_text(tree, tvb, offset, 2, "Header length: %d bytes, %d bits", hdrsize, hdrbits);
    proto_tree_add_text(tree, tvb, offset+2, 2, "Data length: %d bytes", datasize);
    proto_tree_add_text(tree, tvb, offset+4, 1, "Extra data length: %d bytes", extrasize);
    mode = tvb_get_guint8(tvb, offset+5);
    item1 = proto_tree_add_text(tree, tvb, offset+5, 1, "Mode: %d", mode);
    if (mode) {
	tree1 = proto_item_add_subtree (item1, ett_gryphon_flags);
	if (mode & 0x80)
	    proto_tree_add_text(tree1, tvb, offset+5, 1, "1... .... = Transmitted message");
	if (mode & 0x40)
	    proto_tree_add_text(tree1, tvb, offset+5, 1, ".1.. .... = Received message");
	if (mode & 0x20)
	    proto_tree_add_text(tree1, tvb, offset+5, 1, "..1. .... = Local message");
	if (mode & 0x10)
	    proto_tree_add_text(tree1, tvb, offset+5, 1, "...1 .... = Remote message");
	if (mode & 0x01)
	    proto_tree_add_text(tree1, tvb, offset+5, 1, ".... ...1 = Internal message");
    }
    proto_tree_add_text(tree, tvb, offset+6, 1, "Priority: %u",
	tvb_get_guint8(tvb, offset+6));
    proto_tree_add_text(tree, tvb, offset+7, 1, "Error status: %u",
	tvb_get_guint8(tvb, offset+7));
    timestamp = tvb_get_ntohl(tvb, offset+8);
    hours = timestamp /(100000 * 60 *60);
    minutes = (timestamp / (100000 * 60)) % 60;
    seconds = (timestamp / 100000) % 60;
    fraction = timestamp % 100000;
    proto_tree_add_text(tree, tvb, offset+8, 4, "Timestamp: %d:%02d:%02d.%05d", hours, minutes, seconds, fraction);
    proto_tree_add_text(tree, tvb, offset+12, 1, "Context: %u",
	tvb_get_guint8(tvb, offset+12));
    proto_tree_add_text(tree, tvb, offset+13, 3, "reserved:");
    offset += 16;
    item = proto_tree_add_text(pt, tvb, offset, msgsize-16-padding, "Message Body");
    tree = proto_item_add_subtree (item, ett_gryphon_data_body);
    if (hdrsize) {
	proto_tree_add_text(tree, tvb, offset, hdrsize, "Header");
	offset += hdrsize;
    }
    if (datasize) {
	proto_tree_add_text(tree, tvb, offset, datasize, "Data");
	offset += datasize;
    }
    if (extrasize) {
	proto_tree_add_text(tree, tvb, offset, extrasize, "Extra data");
	offset += extrasize;
    }
    if (padding) {
    	proto_tree_add_text(pt, tvb, offset, padding, "padding");
	offset += padding;
    }
    return offset;
}

static int
decode_event(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int     	    hours, minutes, seconds, fraction, padding, length;
    unsigned long   timestamp;
    int		    msgend;

    padding = 3 - (msglen + 3) % 4;
    msgend = offset + msglen;
    proto_tree_add_text(pt, tvb, offset, 1, "Event ID: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 1, "Event context: %u",
	tvb_get_guint8(tvb, offset+1));
    proto_tree_add_text(pt, tvb, offset+2, 2, "reserved");
    offset += 4;
    timestamp = tvb_get_ntohl(tvb, offset);
    hours = timestamp /(100000 * 60 *60);
    minutes = (timestamp / (100000 * 60)) % 60;
    seconds = (timestamp / 100000) % 60;
    fraction = timestamp % 100000;
    proto_tree_add_text(pt, tvb, offset, 4, "Timestamp: %d:%02d:%02d.%05d", hours, minutes, seconds, fraction);
    offset += 4;
    if (offset < msgend) {
    	length = msgend - offset;
    	proto_tree_add_text (pt, tvb, offset, length, "Data (%d bytes)", length);
	offset += length;
    }
    if (padding) {
    	proto_tree_add_text(pt, tvb, offset, padding, "padding");
	offset += padding;
    }
    return offset;
}

static int
cmd_init(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    char    	*ptr;
    
    if (tvb_get_guint8(tvb, offset) == 0)
    	ptr = "Always initialize";
    else
    	ptr = "Initialize if not previously initialized";
    proto_tree_add_text(pt, tvb, offset, 1, "Mode: %s", ptr);
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
eventnum(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    guint8	event = tvb_get_guint8(tvb, offset);
    
    if (event)
    	proto_tree_add_text(pt, tvb, offset, 1, "Event number: %u", event);
    else
    	proto_tree_add_text(pt, tvb, offset, 1, "Event numbers: All");
    proto_tree_add_text(pt, tvb, offset+1, 3, "padding");
    offset += 4;
    return offset;
}

static int
resp_time(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int     hours, minutes, seconds, fraction;
    union {
    	unsigned int		lng[2];
	guint64			lnglng;
    } ts;
    unsigned int    timestamp;
    unsigned char   date[45];
   
    ts.lng[1] = tvb_get_ntohl(tvb, offset);
    ts.lng[0] = tvb_get_ntohl(tvb, offset + 4);
    timestamp = ts.lnglng / 100000L;
    strncpy (date, ctime((time_t*)&timestamp), sizeof(date));
    date[strlen(date)-1] = 0x00;
    proto_tree_add_text(pt, tvb, offset, 8, "Date/Time: %s", date);
    timestamp = ts.lng[0];
    hours = timestamp /(100000 * 60 *60);
    minutes = (timestamp / (100000 * 60)) % 60;
    seconds = (timestamp / 100000) % 60;
    fraction = timestamp % 100000;
    proto_tree_add_text(pt, tvb, offset+4, 4, "Timestamp: %d:%02d:%02d.%05d", hours, minutes, seconds, fraction);
    offset += 8;
    return offset;
}

static int
cmd_setfilt(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int     	    flag = tvb_get_ntohl(tvb, offset);
    int     	    length, padding;
    unsigned char   mode[30];
    
    length =  tvb_get_guint8(tvb, offset+4) + tvb_get_guint8(tvb, offset+5)
	+ tvb_get_ntohs(tvb, offset+6);
    if (flag)
    	strcpy (mode, "Pass");
    else
    	strcpy (mode, "Block");
    if (length == 0)
    	strcat (mode, " all");
    proto_tree_add_text(pt, tvb, offset, 4, "Pass/Block flag: %s", mode);
    proto_tree_add_text(pt, tvb, offset+4, 4, "Length of Pattern & Mask: %d", length);
    offset += 8;
    if (length) {
	proto_tree_add_text(pt, tvb, offset, length * 2, "discarded data");
	offset += length * 2;
    }
    padding = 3 - (length * 2 + 3) % 4;
    if (padding) {
	proto_tree_add_text(pt, tvb, offset+1, 3, "padding");
	offset += padding;
    }
    return offset;
}

static int
cmd_ioctl(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    ioctl, i;

    ioctl = tvb_get_ntohl(tvb, offset);
    for (i = 0; i < SIZEOF(ioctls); i++) {
	if (ioctls[i].value == ioctl)
	    break;
    }
    if (i >= SIZEOF(ioctls))
	i = SIZEOF(ioctls) - 1;
    proto_tree_add_text(pt, tvb, offset, 4, "IOCTL: %s", ioctls[i].strptr);
    offset += 4;
    msglen -= 4;
    if (msglen > 0) {
	proto_tree_add_text(pt, tvb, offset, msglen, "Data");
	offset += msglen;
    }
    return offset;
}

static int
cmd_addfilt(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item;
    proto_tree	*tree;
    guint8	flags;
    int     	blocks, i, length;
    char    	*ptr;
    char    	pass[] = ".... ...1 = Conforming messages are passed";
    char    	block[] = ".... ...0 = Conforming messages are blocked";
    char    	active[] = ".... ..1. = The filter is active";
    char    	inactive[] = ".... ..0. = The filter is inactive";

    item = proto_tree_add_text(pt, tvb, offset, 1, "Flags");
    tree = proto_item_add_subtree (item, ett_gryphon_flags);
    flags = tvb_get_guint8(tvb, offset);
    if (flags & FILTER_PASS_FLAG)
	ptr = pass;
    else
	ptr = block;
    proto_tree_add_text(tree, tvb, offset, 1, ptr);
    if (flags & FILTER_ACTIVE_FLAG)
	ptr = active;
    else
	ptr = inactive;
    proto_tree_add_text(tree, tvb, offset, 1, ptr);
    offset += 1;
    msglen -= 1;
    blocks = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(pt, tvb, offset, 1, "Number of filter blocks = %d", blocks);
    proto_tree_add_text(pt, tvb, offset+1, 6, "reserved");
    offset += 7;
    msglen -= 7;
    for (i = 1; i <= blocks; i++) {
	length = tvb_get_ntohs(tvb, offset+2) * 2 + 8;
	length += 3 - (length + 3) % 4;
	item = proto_tree_add_text(pt, tvb, offset, length, "Filter block %d", i);
	tree = proto_item_add_subtree (item, ett_gryphon_cmd_filter_block);
	offset = filter_block(tvb, offset, src, msglen, tree);
    }
    return offset;
}

static int
resp_addfilt(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 1, "Filter handle: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
cmd_modfilt(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    guint8	    filter_handle;
    unsigned char   action, i;

    filter_handle = tvb_get_guint8(tvb, offset);
    if (filter_handle)
    	proto_tree_add_text(pt, tvb, offset, 1, "Filter handle: %u",
	    filter_handle);
    else
    	proto_tree_add_text(pt, tvb, offset, 1, "Filter handles: all");
    action = tvb_get_guint8(tvb, offset + 1);
    for (i = 0; i < SIZEOF(filtacts); i++) {
	if (filtacts[i].value == action)
	    break;
    }
    if (i >= SIZEOF(filtacts))
	i = SIZEOF(filtacts) - 1;
    proto_tree_add_text(pt, tvb, offset+1, 1, "Action: %s filter", filtacts[i].strptr);
    proto_tree_add_text(pt, tvb, offset+2, 2, "reserved");
    offset += 4;
    return offset;
}

static int
resp_filthan(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int     	handles = tvb_get_guint8(tvb, offset);
    int     	i, padding;
    
    proto_tree_add_text(pt, tvb, offset, 1, "Number of filter handles: %d", handles);
    for (i = 1; i <= handles; i++){
	proto_tree_add_text(pt, tvb, offset+i, 1, "Handle %d: %u", i,
	    tvb_get_guint8(tvb, offset+i));
    }
    padding = 3 - (handles + 1 + 3) % 4;
    if (padding)
	proto_tree_add_text(pt, tvb, offset+1+handles, padding, "padding");
    offset += 1+handles+padding;
    return offset;
}

static int
dfiltmode(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    i;
    unsigned char   mode;
    
    mode = tvb_get_guint8(tvb, offset);
    for (i = 0; i < SIZEOF(modes); i++) {
	if (dmodes[i].value == mode)
	    break;
    }
    if (i >= SIZEOF(dmodes))
	i = SIZEOF(dmodes) - 1;
    proto_tree_add_text(pt, tvb, offset, 1, "Filter mode: %s", dmodes[i].strptr);
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
filtmode(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    i;
    unsigned char   mode;
    
    mode = tvb_get_guint8(tvb, offset);
    for (i = 0; i < SIZEOF(modes); i++) {
	if (modes[i].value == mode)
	    break;
    }
    if (i >= SIZEOF(modes))
	i = SIZEOF(modes) - 1;
    proto_tree_add_text(pt, tvb, offset, 1, "Filter mode: %s", modes[i].strptr);
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
resp_events(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    i;
    proto_tree	    *tree;
    proto_item	    *item;
    
    i = 1;
    while (msglen != 0) {
	item = proto_tree_add_text(pt, tvb, offset, 20, "Event %d:", i);
	tree = proto_item_add_subtree (item, ett_gryphon_cmd_events_data);
	proto_tree_add_text(tree, tvb, offset, 1, "Event ID: %u",
	    tvb_get_guint8(tvb, offset));
	proto_tree_add_text(tree, tvb, offset+1, 19, "Event name: %.19s",
		tvb_get_ptr(tvb, offset+1, 19));
	offset += 20;
	msglen -= 20;
	i++;
    }
    return offset;
}

static int
cmd_register(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 16, "Username: %.16s",
	tvb_get_ptr(tvb, offset, 16));
    offset += 16;
    proto_tree_add_text(pt, tvb, offset, 32, "Password: %.32s",
	tvb_get_ptr(tvb, offset, 32));
    offset += 32;
    return offset;
}

static int
resp_register(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 1, "Client ID: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 1, "Privileges: %u",
	tvb_get_guint8(tvb, offset+1));
    proto_tree_add_text(pt, tvb, offset+2, 2, "reserved");
    offset += 4;
    return offset;
}


static int
resp_getspeeds(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int size;
    int number;
    int index;
    
    proto_tree_add_text(pt, tvb, offset, 4, "Set Speed IOCTL");
    proto_tree_add_text(pt, tvb, offset+4, 4, "Get Speed IOCTL");
    size = tvb_get_guint8(tvb, offset+8);
    proto_tree_add_text(pt, tvb, offset+8, 1, "Speed data size is %d bytes", size);
    number = tvb_get_guint8(tvb, offset+9);
    proto_tree_add_text(pt, tvb, offset+9, 1, "There are %d preset speeds", number);
    offset += 10;
    for (index = 0; index < number; index++) {
	proto_tree_add_text(pt, tvb, offset, size, "Data for preset %d",
	    index+1);
	offset += size;
    }
    return offset;
}

static int
cmd_sort(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    char    	*which;
    
    which = tvb_get_guint8(tvb, offset) ?
	    "Sort into blocks of up to 16 messages" :
	    "Do not sort messages";
    proto_tree_add_text(pt, tvb, offset, 1, "Set sorting: %s", which);
    offset += 1;
    return offset;
}

static int
cmd_optimize(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    char    	*which;
    
    which = tvb_get_guint8(tvb, offset) ? 
	    "Optimize for latency (Nagle algorithm disabled)" :
	    "Optimize for throughput (Nagle algorithm enabled)";
    proto_tree_add_text(pt, tvb, offset, 1, "Set optimization: %s", which);
    offset += 1;
    return offset;
}

static int
resp_config(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*ti;
    proto_tree	*ft;
    int     	devices;
    int     	i;
    unsigned int j, x;
    
    static const value_string protocol_types[] = {
	{GDUMMY * 256 + GDGDMARKONE,	"Dummy device driver"},
	{GCAN * 256 + G82527,	    	"CAN, 82527 subtype"},
	{GCAN * 256 + GSJA1000,     	"CAN, SJA1000 subtype"},
	{GCAN * 256 + G82527SW,     	"CAN, 82527 single wire subtype"},
	{GJ1850 * 256 + GHBCCPAIR,  	"J1850, HBCC subtype"},
	{GJ1850 * 256 + GDLC,	    	"J1850, GM DLC subtype"},
	{GJ1850 * 256 + GCHRYSLER,  	"J1850, Chrysler subtype"},
	{GJ1850 * 256 + GDEHC12,    	"J1850, DE HC12 KWP/BDLC subtype"},
	{GKWP2000 * 256 + GDEHC12KWP,  	"Keyword protocol 2000"},
	{GHONDA * 256 + GDGHC08,    	"Honda UART, DG HC08 subtype"},
	{GFORDUBP * 256 + GDGUBP08, 	"Ford UBP, DG HC08 subtype"},
	{-1,	    	    	    	"- unknown -"},
    };

    proto_tree_add_text(pt, tvb, offset, 20, "Device name: %.20s",
	tvb_get_ptr(tvb, offset, 20));
    offset += 20;

    proto_tree_add_text(pt, tvb, offset, 8, "Device version: %.8s",
	tvb_get_ptr(tvb, offset, 8));
    offset += 8;

    proto_tree_add_text(pt, tvb, offset, 20, "Device serial number: %.20s",
	tvb_get_ptr(tvb, offset, 20));
    offset += 20;

    devices = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(pt, tvb, offset, 1, "Number of channels: %d", devices);
    proto_tree_add_text(pt, tvb, offset+1, 15, "reserved");
    offset += 16;
    for (i = 1; i <= devices; i++) {
	ti = proto_tree_add_text(pt, tvb, offset, 80, "Channel %d:", i);
	ft = proto_item_add_subtree(ti, ett_gryphon_cmd_config_device);
	proto_tree_add_text(ft, tvb, offset, 20, "Driver name: %.20s",
	    tvb_get_ptr(tvb, offset, 20));
	offset += 20;

	proto_tree_add_text(ft, tvb, offset, 8, "Driver version: %.8s",
	    tvb_get_ptr(tvb, offset, 8));
	offset += 8;

	proto_tree_add_text(ft, tvb, offset, 24, "Device security string: %.24s",
	    tvb_get_ptr(tvb, offset, 24));
	offset += 24;

	proto_tree_add_text(ft, tvb, offset, 20, "Hardware serial number: %.20s",
	    tvb_get_ptr(tvb, offset, 20));
	offset += 20;

    	x = tvb_get_ntohs(tvb, offset);
	for (j = 0; j < SIZEOF(protocol_types); j++) {
	    if (protocol_types[j].value == x)
	    	break;
	}
	if (j >= SIZEOF(protocol_types))
	    j = SIZEOF(protocol_types) -1;
	proto_tree_add_text(ft, tvb, offset, 2, "Protocol type & subtype: %s", protocol_types[j].strptr);
	offset += 2;

	proto_tree_add_text(ft, tvb, offset, 1, "Channel ID: %u",
	    tvb_get_guint8(tvb, offset));
	proto_tree_add_text(ft, tvb, offset+1, 5, "reserved");
	offset += 6;
    }
    return offset;
}

static int
cmd_sched(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	    *item, *item1;
    proto_tree	    *tree, *tree1;
    int		    save_offset;
    unsigned int    i, x, length;
    unsigned char   def_chan = tvb_get_guint8(tvb, offset-9);
    char    	    *ptr;
    char    	    crit[] = ".... ...1 = Critical scheduler";
    char    	    norm[] = ".... ...0 = Normal scheduler";
    
    x = tvb_get_ntohl(tvb, offset);
    if (x == 0xFFFFFFFF)
    	proto_tree_add_text(pt, tvb, offset, 4, "Number of iterations: infinite");
    else
    	proto_tree_add_text(pt, tvb, offset, 4, "Number of iterations: %d", x);
    offset += 4;
    msglen -= 4;
    x = tvb_get_ntohl(tvb, offset);
    item = proto_tree_add_text(pt, tvb, offset, 4, "Flags");
    tree = proto_item_add_subtree (item, ett_gryphon_flags);
    ptr = x & 1 ? crit : norm;
    proto_tree_add_text(tree, tvb, offset, 4, ptr, NULL);
    offset += 4;
    msglen -= 4;
    i = 1;
    while (msglen > 0) {
    	length = 16 + tvb_get_guint8(tvb, offset+16) +
    	    tvb_get_ntohs(tvb, offset+18) + tvb_get_guint8(tvb, offset+20) + 16;
	length += 3 - (length + 3) % 4;
	item = proto_tree_add_text(pt, tvb, offset, length, "Message %d", i);
	tree = proto_item_add_subtree (item, ett_gryphon_cmd_sched_data);
	x = tvb_get_ntohl(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 4, "Sleep: %d milliseconds", x);
	offset += 4;
	msglen -= 4;
	x = tvb_get_ntohl(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 4, "Transmit count: %d", x);
	offset += 4;
	msglen -= 4;
	x = tvb_get_ntohl(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 4, "Transmit period: %d milliseconds", x);
	offset += 4;
	msglen -= 4;
	proto_tree_add_text(tree, tvb, offset, 2, "reserved flags");
	x = tvb_get_guint8(tvb, offset+2);
	if (x == 0)
	    x = def_chan;
	proto_tree_add_text(tree, tvb, offset+2, 1, "Channel: %d", x);
	proto_tree_add_text(tree, tvb, offset+3, 1, "reserved");
	offset += 4;
	msglen -= 4;
	item1 = proto_tree_add_text(tree, tvb, offset, length, "Message");
	tree1 = proto_item_add_subtree (item1, ett_gryphon_cmd_sched_cmd);
	save_offset = offset;
	offset = decode_data(tvb, offset, msglen, src, tree1);
	msglen -= offset - save_offset;
	i++;
    }
    return offset;
}

static int
resp_blm_data(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    i;
    int             hours, minutes, seconds, fraction, x, fract;
    unsigned long   timestamp;
    char    *fields[] = {
    	"Bus load average: %d.%02d%%",
    	"Current bus load: %d.%02d%%",
    	"Peak bus load: %d.%02d%%",
    	"Historic peak bus load: %d.%02d%%"
    };

    timestamp = tvb_get_ntohl(tvb, offset);
    hours = timestamp /(100000 * 60 *60);
    minutes = (timestamp / (100000 * 60)) % 60;
    seconds = (timestamp / 100000) % 60;
    fraction = timestamp % 100000;
    proto_tree_add_text(pt, tvb, offset, 4, "Timestamp: %d:%02d:%02d.%05d", hours, minutes, seconds, fraction);
    offset += 4;
    for (i = 0; i < SIZEOF(fields); i++){
    	x = tvb_get_ntohs(tvb, offset);
	fract = x % 100;
	x /= 100;
	proto_tree_add_text(pt, tvb, offset, 2, fields[i], x, fract);
	offset += 2;
    }
    return offset;
}

static int
resp_blm_stat(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    x, i;
    char    	    *fields[] = {
    	"Receive frame count: %d",
    	"Transmit frame count: %d",
    	"Receive dropped frame count: %d",
    	"Transmit dropped frame count: %d",
    	"Receive error count: %d",
    	"Transmit error count: %d",
    };

    offset = resp_blm_data(tvb, offset, src, msglen, pt);
    for (i = 0; i < SIZEOF(fields); i++){
	x = tvb_get_ntohl(tvb, offset);
	proto_tree_add_text(pt, tvb, offset, 4, fields[i], x);
	offset += 4;
    }
    return offset;
}

static int
cmd_addresp(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item;
    proto_tree	*tree;
    int     	blocks, responses, old_handle, i, length;
    int     	action, actionType, actionValue;
    char    	*ptr;
    char    	active[] = ".... ..1. = The response is active";
    char    	inactive[] = ".... ..0. = The response is inactive";

    actionType = 0;
    item = proto_tree_add_text(pt, tvb, offset, 1, "Flags");
    tree = proto_item_add_subtree (item, ett_gryphon_flags);
    if (tvb_get_guint8(tvb, offset) & FILTER_ACTIVE_FLAG)
    	ptr = active;
    else
    	ptr = inactive;
    proto_tree_add_text(tree, tvb, offset, 1, ptr, NULL);
    offset += 1;
    blocks = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(pt, tvb, offset, 1, "Number of filter blocks = %d", blocks);
    offset += 1;
    responses = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(pt, tvb, offset, 1, "Number of response blocks = %d", responses);
    offset += 1;
    old_handle = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(pt, tvb, offset, 1, "Old handle = %d", old_handle);
    offset += 1;
    action = tvb_get_guint8(tvb, offset);
    switch (action & 7) {
    case FR_RESP_AFTER_EVENT:
    	ptr = "Send response(s) for each conforming message";
    	break;
    case FR_RESP_AFTER_PERIOD:
    	ptr = "Send response(s) after the specified period expires following a conforming message";
    	break;
    case FR_IGNORE_DURING_PER:
    	ptr = "Send response(s) for a conforming message and ignore\nfurther messages until the specified period expires";
    	break;
    default:
    	ptr = "- unknown -";
    }
    item = proto_tree_add_text(pt, tvb, offset, 1, "Action = %s", ptr);
    tree = proto_item_add_subtree (item, ett_gryphon_flags);
    if (action & FR_DEACT_AFTER_PER && !(action & FR_DELETE)){
    	proto_tree_add_text(tree, tvb, offset, 1,
	    	"1.0. .... Deactivate this response after the specified period following a conforming message");
    }
    if (action & FR_DEACT_ON_EVENT && !(action & FR_DELETE)){
    	proto_tree_add_text(tree, tvb, offset, 1,
	    	".10. .... Deactivate this response for a conforming message");
    }
    if (action & FR_DEACT_AFTER_PER && action & FR_DELETE){
    	proto_tree_add_text(tree, tvb, offset, 1,
	    	"1.1. .... Delete this response after the specified period following a conforming message");
    }
    if (action & FR_DEACT_ON_EVENT && action & FR_DELETE){
    	proto_tree_add_text(tree, tvb, offset, 1,
	    	".11. .... Delete this response for a conforming message");
    }
    actionValue = tvb_get_ntohs(tvb, offset+2);
    if (actionValue) {
	if (action & FR_PERIOD_MSGS){
    	    ptr = "...1 .... The period is in frames";
	    actionType = 1;
	} else {
    	    ptr = "...0 .... The period is in 0.01 seconds";
	    actionType = 0;
	}
    	proto_tree_add_text(tree, tvb, offset, 1, ptr, NULL);
    }
    offset += 1;
    proto_tree_add_text(pt, tvb, offset, 1, "reserved");
    offset += 1;
    if (actionValue) {
    	if (actionType == 1) {
	    proto_tree_add_text(tree, tvb, offset, 2, "Period: %d messages", actionValue);
	} else {
	    proto_tree_add_text(tree, tvb, offset, 2, "Period: %d.%02d seconds", actionValue/100, actionValue%100);
	}
    }
    offset += 2;
    for (i = 1; i <= blocks; i++) {
	length = tvb_get_ntohs(tvb, offset+2) * 2 + 8;
	length += 3 - (length + 3) % 4;
	item = proto_tree_add_text(pt, tvb, offset, length, "Filter block %d", i);
	tree = proto_item_add_subtree (item, ett_gryphon_cmd_filter_block);
	offset = filter_block(tvb, offset, src, msglen, tree);
    }
    for (i = 1; i <= responses; i++) {
	length = tvb_get_ntohs(tvb, offset+4) + 8;
	length += 3 - (length + 3) % 4;
	item = proto_tree_add_text(pt, tvb, offset, length, "Response block %d", i);
	tree = proto_item_add_subtree (item, ett_gryphon_cmd_response_block);
	dissect_gryphon_message(tvb, offset, tree, TRUE);
	offset += length;
    }
    return offset;
}

static int
resp_addresp(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 1, "Response handle: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
cmd_modresp(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned char   action;
    unsigned char   dest = tvb_get_guint8(tvb, offset-5);
    guint8	    resp_handle;
    unsigned int    i;

    resp_handle = tvb_get_guint8(tvb, offset);
    if (resp_handle)
	proto_tree_add_text(pt, tvb, offset, 1, "Response handle: %u",
	    resp_handle);
    else if (dest)
	proto_tree_add_text(pt, tvb, offset, 1, "Response handles: all on channel %hd", dest);
    else
    	proto_tree_add_text(pt, tvb, offset, 1, "Response handles: all");
    action = tvb_get_guint8(tvb, offset+1);
    for (i = 0; i < SIZEOF(filtacts); i++) {
	if (filtacts[i].value == action)
	    break;
    }
    if (i >= SIZEOF(filtacts))
	i = SIZEOF(filtacts) - 1;
    proto_tree_add_text(pt, tvb, offset+1, 1, "Action: %s response", filtacts[i].strptr);
    proto_tree_add_text(pt, tvb, offset+2, 2, "reserved");
    offset += 4;
    return offset;
}

static int
resp_resphan(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    int     	handles = tvb_get_guint8(tvb, offset);
    int     	i, padding;
    
    proto_tree_add_text(pt, tvb, offset, 1, "Number of response handles: %d", handles);
    for (i = 1; i <= handles; i++){
	proto_tree_add_text(pt, tvb, offset+i, 1, "Handle %d: %u", i,
	    tvb_get_guint8(tvb, offset+i));
    }
    padding = 3 - (handles + 1 + 3) % 4;
    if (padding)
	proto_tree_add_text(pt, tvb, offset+1+handles, padding, "padding");
    offset += 1+handles+padding;
    return offset;
}

static int
resp_sched(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    id = tvb_get_ntohl(tvb, offset);

    proto_tree_add_text(pt, tvb, offset, 4, "Transmit schedule ID: %u", id);
    offset += 4;
    return offset;
}

static int
cmd_desc(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 4, "Program size: %u bytes",
	tvb_get_ntohl(tvb, offset));
    offset += 4;
    proto_tree_add_text(pt, tvb, offset, 32, "Program name: %.32s",
	tvb_get_ptr(tvb, offset, 32));
    offset += 32;
    proto_tree_add_text(pt, tvb, offset, 80, "Program description: %.80s",
	tvb_get_ptr(tvb, offset, 80));
    offset += 80;
    return offset;
}

static int
resp_desc(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item;
    proto_tree	*tree;
    char    	*ptr;
    char    	missing[] = ".... ...0 = The program is not present";
    char    	present[] = ".... ...1 = The program is already present";
    
    item = proto_tree_add_text(pt, tvb, offset, 1, "Flags");
    tree = proto_item_add_subtree (item, ett_gryphon_flags);
    if (tvb_get_guint8(tvb, offset) & 1)
    	ptr = present;
    else
    	ptr = missing;
    proto_tree_add_text(tree, tvb, offset, 1, ptr);
    proto_tree_add_text(pt, tvb, offset+1, 1, "Handle: %u",
	tvb_get_guint8(tvb, offset+1));
    proto_tree_add_text(pt, tvb, offset+2, 2, "reserved");
    offset += 4;
    return offset;
}

static int
cmd_upload(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    length;
    
    proto_tree_add_text(pt, tvb, offset, 2, "Block number: %u",
	tvb_get_ntohs(tvb, offset));
    offset += 4;
    msglen -= 4;
    proto_tree_add_text(pt, tvb, offset+2, 1, "Handle: %u",
	tvb_get_guint8(tvb, offset+2));
    offset += 3;
    msglen -= 3;
    length = msglen;
    proto_tree_add_text(pt, tvb, offset, length, "Data (%d bytes)", length);
    offset += length;
    length = 3 - (length + 3) % 4;
    if (length) {
	proto_tree_add_text(pt, tvb, offset, length, "padding");
	offset += length;
    }
    return offset;
}

static int
cmd_delete(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 32, "Program name: %.32s",
	tvb_get_ptr(tvb, offset, 32));
    offset += 32;
    return offset;
}

static int
cmd_list(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 1, "Block number: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
resp_list(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item;
    proto_tree	*tree;
    unsigned int    i, count;
    
    count = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(pt, tvb, offset, 1, "Number of programs in this response: %d", count);
    proto_tree_add_text(pt, tvb, offset+1, 1, "reserved");
    offset += 2;
    proto_tree_add_text(pt, tvb, offset, 2, "Number of remaining programs: %u",
	tvb_get_ntohs(tvb, offset));
    offset += 2;
    for (i = 1; i <= count; i++) {
	item = proto_tree_add_text(pt, tvb, offset, 112, "Program %d", i);
	tree = proto_item_add_subtree (item, ett_gryphon_pgm_list);
	proto_tree_add_text(tree, tvb, offset, 32, "Name: %.32s",
	    tvb_get_ptr(tvb, offset, 32));
	offset += 32;
	proto_tree_add_text(tree, tvb, offset, 80, "Description: %.80s",
	    tvb_get_ptr(tvb, offset, 80));
	offset += 80;
    }
    return offset;
}

static int
cmd_start(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    char	    string[120];
    gint	    length;
    
    offset = cmd_delete(tvb, offset, src, msglen, pt);
    length = tvb_get_nstringz0(tvb, offset, 120, string) + 1;
    proto_tree_add_text(pt, tvb, offset, length, "Arguments: %s", string);
    offset += length;
    length = 3 - (length + 3) % 4;
    if (length) {
	proto_tree_add_text(pt, tvb, offset, length, "padding");
	offset += length;
    }
    return offset;
}

static int
resp_start(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 1, "Channel (Client) number: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
resp_status(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item;
    proto_tree	*tree;
    unsigned int    i, copies, length;
    
    copies = tvb_get_guint8(tvb, offset);
    item = proto_tree_add_text(pt, tvb, offset, 1, "Number of running copies: %d", copies);
    tree = proto_item_add_subtree (item, ett_gryphon_pgm_status);
    offset += 1;
    if (copies) {
	for (i = 1; i <= copies; i++) {
	    proto_tree_add_text(tree, tvb, offset, 1, "Program %d channel (client) number %u",
		i, tvb_get_guint8(tvb, offset));
	    offset += 1;
	}
    }
    length = 3 - (copies + 1 + 3) % 4;
    if (length) {
	proto_tree_add_text(pt, tvb, offset, length, "padding");
	offset += length;
    }
    return offset;
}

static int
cmd_options(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_item	*item;
    proto_tree	*tree;
    unsigned int    i, size, padding, option, option_length, option_value;
    unsigned char   *string, *string1;
    
    item = proto_tree_add_text(pt, tvb, offset, 1, "Handle: %u",
	tvb_get_guint8(tvb, offset));
    item = proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    msglen -= 4;
    for (i = 1; msglen > 0; i++) {
	option_length = tvb_get_guint8(tvb, offset+1);
    	size = option_length + 2;
	padding = 3 - ((size + 3) %4);
	item = proto_tree_add_text(pt, tvb, offset, size + padding, "Option number %d", i);
    	tree = proto_item_add_subtree (item, ett_gryphon_pgm_options);
	option = tvb_get_guint8(tvb, offset);
	switch (option_length) {
	case 1:
	    option_value = tvb_get_guint8(tvb, offset+2);
	    break;
	case 2:
	    option_value = tvb_get_ntohs(tvb, offset+2);
	    break;
	case 4:
	    option_value = tvb_get_ntohl(tvb, offset+2);
	    break;
	default:
	    option_value = 0;
	}
	string = "unknown option";
	string1 = "unknown option data";
	switch (option) {
	case PGM_CONV:
	    string = "Type of data in the file";
	    switch (option_value) {
	    case PGM_BIN:
	    	string1 = "Binary - Don't modify";
	    	break;
	    case PGM_ASCII:
	    	string1 = "ASCII - Remove CR's";
	    	break;
	    }
	    break;
	case PGM_TYPE:
	    string = "Type of file";
	    switch (option_value) {
	    case PGM_PGM:
	    	string1 = "Executable";
	    	break;
	    case PGM_DATA:
	    	string1 = "Data";
	    	break;
	    }
	    break;
	}
	proto_tree_add_text(tree, tvb, offset, 1, "%s", string);
	proto_tree_add_text(tree, tvb, offset+2, option_length, "%s", string1);
	if (padding)
	    proto_tree_add_text(tree, tvb, offset+option_length+2, padding, "padding");
	offset += size + padding;
	msglen -= size + padding;
    }
    return offset;
}

static int
cmd_files(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    u_char  *which;
    
    if (tvb_get_guint8(tvb, offset) == 0)
	which = "First group of names";
    else
	which = "Subsequent group of names";
    
    proto_tree_add_text(pt, tvb, offset, 1, "%s", which);
    proto_tree_add_text(pt, tvb, offset+1, msglen-1, "Directory: %.*s",
	msglen-1, tvb_get_ptr(tvb, offset+1, msglen-1));
    offset += msglen;
    return offset;
}

static int
resp_files(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    u_char  	*flag;
    
    flag = tvb_get_guint8(tvb, offset) ? "Yes": "No";
    proto_tree_add_text(pt, tvb, offset, 1, "More filenames to return: %s", flag);
    proto_tree_add_text(pt, tvb, offset+1, msglen-1, "File and directory names");
    offset += msglen;
    return offset;
}

static int
cmd_usdt(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    u_char  *desc;
    guint8  assemble_flag;
    
    if (tvb_get_guint8(tvb, offset))
	desc = "Register with gusdt";
    else
	desc = "Unregister with gusdt";
    proto_tree_add_text(pt, tvb, offset, 1, "%s", desc);
    
    if (tvb_get_guint8(tvb, offset+1))
	desc = "Echo long transmit messages back to the client";
    else
	desc = "Do not echo long transmit messages back to the client";
    proto_tree_add_text(pt, tvb, offset+1, 1, "%s", desc);
    
    assemble_flag = tvb_get_guint8(tvb, offset+2);
    if (assemble_flag == 2)
    	desc = "Assemble long received messages but do not send them to the client";
    else if (assemble_flag)
    	desc = "Assemble long received messages and send them to the client";
    else
    	desc = "Do not assemble long received messages on behalf of the client";
    proto_tree_add_text(pt, tvb, offset+2, 1, "%s", desc);
    
    offset += 4;
    return offset;
}

static int
speed(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    proto_tree_add_text(pt, tvb, offset, 1, "Baud rate index: %u",
	tvb_get_guint8(tvb, offset));
    proto_tree_add_text(pt, tvb, offset+1, 3, "reserved");
    offset += 4;
    return offset;
}

static int
filter_block(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    unsigned int    type, operator, i;
    int     length, padding;
    
    proto_tree_add_text(pt, tvb, offset, 2, "Filter field starts at byte %d",
	tvb_get_ntohs(tvb, offset));
    length = tvb_get_ntohs(tvb, offset+2);
    proto_tree_add_text(pt, tvb, offset+2, 2, "Filter field is %d bytes long", length);
    type = tvb_get_guint8(tvb, offset+4);
    for (i = 0; i < SIZEOF(filter_data_types); i++) {
	if (filter_data_types[i].value == type)
	    break;
    }
    if (i >= SIZEOF(filter_data_types))
	i = SIZEOF(filter_data_types) - 1;
    proto_tree_add_text(pt, tvb, offset+4, 1, "Filtering on %s", filter_data_types[i].strptr);

    operator = tvb_get_guint8(tvb, offset+5);
    for (i = 0; i < SIZEOF(operators); i++) {
	if (operators[i].value == operator)
	    break;
    }
    if (i >= SIZEOF(operators))
	i = SIZEOF(operators) - 1;
    proto_tree_add_text(pt, tvb, offset+5, 1, "Type of comparison: %s", operators[i].strptr);
    proto_tree_add_text(pt, tvb, offset+6, 2, "reserved");
    offset += 8;
    
    if (operator == BIT_FIELD_CHECK) {
	proto_tree_add_text(pt, tvb, offset, length, "Pattern");
	proto_tree_add_text(pt, tvb, offset+length, length, "Mask");
    } else {
	switch (length) {
	case 1:
	    proto_tree_add_text(pt, tvb, offset, 1, "Value: %u",
		tvb_get_guint8(tvb, offset));
	    break;
	case 2:
	    proto_tree_add_text(pt, tvb, offset, 2, "Value: %u",
		tvb_get_ntohs(tvb, offset));
	    break;
	case 4:
	    proto_tree_add_text(pt, tvb, offset, 4, "Value: %u",
		tvb_get_ntohl(tvb, offset));
	    break;
	default:
	    proto_tree_add_text(pt, tvb, offset, length, "Value");
	}
    }
    offset += length * 2;
    padding = 3 - (length * 2 + 3) % 4;
    if (padding) {
	proto_tree_add_text(pt, tvb, offset, padding, "padding");
	offset += padding;
    }
    return offset;
}

static int
blm_mode(tvbuff_t *tvb, int offset, int src, int msglen, proto_tree *pt)
{
    char    *mode, line[50];
    int     x, y, seconds;
    
    x = tvb_get_ntohl(tvb, offset);
    y = tvb_get_ntohl(tvb, offset+4);
    switch (x) {
    case 0:
    	mode = "Off";
	sprintf (line, "reserved");
    	break;
    case 1:
    	mode = "Average over time";
	seconds = y / 1000;
	y = y % 1000;
	sprintf (line, "Averaging period: %d.%03d seconds", seconds, y);
    	break;
    case 2:
    	mode = "Average over frame count";
	sprintf (line, "Averaging period: %d frames", y);
    	break;
    default:
    	mode = "- unknown -";
	sprintf (line, "reserved");
    }
    proto_tree_add_text(pt, tvb, offset, 4, "Mode: %s", mode);
    offset += 4;
    proto_tree_add_text(pt, tvb, offset, 4, line, NULL);
    offset += 4;
    return offset;
}

void
proto_register_gryphon(void)
{
    static hf_register_info hf[] = {
	{ &hf_gryph_src,
	{ "Source",           "gryph.src", FT_UINT8, BASE_DEC, NULL, 0x0,
	    	"", HFILL }},
	{ &hf_gryph_srcchan,
	{ "Source channel",   "gryph.srcchan", FT_UINT8, BASE_DEC, NULL, 0x0,
	    	"", HFILL }},
	{ &hf_gryph_dest,
	{ "Destination",      "gryph.dest", FT_UINT8, BASE_DEC, NULL, 0x0,
	    	"", HFILL }},
	{ &hf_gryph_destchan,
	{ "Destination channel", "gryph.dstchan", FT_UINT8, BASE_DEC, NULL, 0x0,
	    	"", HFILL }},
	{ &hf_gryph_type,
	{ "Frame type",       "gryph.type", FT_UINT8, BASE_DEC, NULL, 0x0,
	    	"", HFILL }},
	{ &hf_gryph_cmd,
	{ "Command",          "gryph.cmd.cmd", FT_UINT8, BASE_DEC, NULL, 0x0,
	    	"", HFILL }},
    };

    static gint *ett[] = {
	&ett_gryphon,
	&ett_gryphon_header,
	&ett_gryphon_body,
	&ett_gryphon_command_data,
	&ett_gryphon_response_data,
	&ett_gryphon_data_header,
	&ett_gryphon_flags,
	&ett_gryphon_data_body,
	&ett_gryphon_cmd_filter_block,
	&ett_gryphon_cmd_events_data,
	&ett_gryphon_cmd_config_device,
	&ett_gryphon_cmd_sched_data,
	&ett_gryphon_cmd_sched_cmd,
	&ett_gryphon_cmd_response_block,
	&ett_gryphon_pgm_list,
	&ett_gryphon_pgm_status,
	&ett_gryphon_pgm_options,
    };
    proto_gryphon = proto_register_protocol("DG Gryphon Protocol",
					    "Gryphon",
					    "gryphon");
    proto_register_field_array(proto_gryphon, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_gryphon(void)
{
    dissector_add("tcp.port", 7000, &dissect_gryphon, proto_gryphon);
}

/* Start the functions we need for the plugin stuff */
G_MODULE_EXPORT void
plugin_reg_handoff(void){
  proto_reg_handoff_gryphon();
}

G_MODULE_EXPORT void
plugin_init(plugin_address_table_t *pat){
  /* initialise the table of pointers needed in Win32 DLLs */
  plugin_address_table_init(pat);
  /* register the new protocol, protocol fields, and subtrees */
  if (proto_gryphon == -1) { /* execute protocol initialization only once */
    proto_register_gryphon();
  }
}
/* End the functions we need for plugin stuff */
