From 760d0a2a75aba6307550bf551c5eb06174c256b5 Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Thu, 10 Mar 2011 18:48:11 +0000 Subject: xpp: use 'xtalk' for the USB access code * Move most of the USB access code from xpp/ to xpp/xtalk/ . * astribank_tool and such tools can now use a shorter -D mmm/nnn rather than a full path. Signed-off-by: Oron Peled Acked-by: Tzafrir Cohen git-svn-id: http://svn.asterisk.org/svn/dahdi/tools/trunk@9825 a0bf4364-ded3-4de4-8d8a-66a801d63aff --- xpp/xtalk/xtalk.c | 497 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 497 insertions(+) create mode 100644 xpp/xtalk/xtalk.c (limited to 'xpp/xtalk/xtalk.c') diff --git a/xpp/xtalk/xtalk.c b/xpp/xtalk/xtalk.c new file mode 100644 index 0000000..d3da5e0 --- /dev/null +++ b/xpp/xtalk/xtalk.c @@ -0,0 +1,497 @@ +/* + * Written by Oron Peled + * Copyright (C) 2009, Xorcom + * + * All rights reserved. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char rcsid[] = "$Id$"; + +#define DBG_MASK 0x02 + +#define TIMEOUT 6000 + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_device { + void *transport_priv; /* e.g: struct xusb */ + struct xtalk_ops ops; + struct xtalk_protocol xproto; + uint8_t xtalk_proto_version; + uint8_t status; + size_t packet_size; + uint16_t tx_sequenceno; +}; + +CMD_DEF(XTALK, ACK, + uint8_t stat; + ); + +CMD_DEF(XTALK, PROTO_GET, + uint8_t proto_version; + uint8_t reserved; + ); + +CMD_DEF(XTALK, PROTO_GET_REPLY, + uint8_t proto_version; + uint8_t reserved; + ); + +union XTALK_PDATA(XTALK) { + MEMBER(XTALK, ACK); + MEMBER(XTALK, PROTO_GET); + MEMBER(XTALK, PROTO_GET_REPLY); +} PACKED members; + +struct xtalk_protocol xtalk_base = { + .name = "XTALK", + .proto_version = 0, + .commands = { + CMD_RECV(XTALK, ACK, NULL), + CMD_SEND(XTALK, PROTO_GET), + CMD_RECV(XTALK, PROTO_GET_REPLY, NULL), + }, + .ack_statuses = { + ACK_STAT(OK, "Acknowledges previous command"), + ACK_STAT(FAIL, "Last command failed"), + ACK_STAT(RESET_FAIL, "reset failed"), + ACK_STAT(NODEST, "No destination is selected"), + ACK_STAT(MISMATCH, "Data mismatch"), + ACK_STAT(NOACCESS, "No access"), + ACK_STAT(BAD_CMD, "Bad command"), + ACK_STAT(TOO_SHORT, "Packet is too short"), + ACK_STAT(ERROFFS, "Offset error (not used)"), + ACK_STAT(NO_LEEPROM, "Large EEPROM was not found"), + ACK_STAT(NO_EEPROM, "No EEPROM was found"), + ACK_STAT(WRITE_FAIL, "Writing to device failed"), + ACK_STAT(NOPWR_ERR, "No power on USB connector"), + } +}; + +void free_command(struct xtalk_command *cmd) +{ + if(!cmd) + return; + memset(cmd, 0, cmd->header.len); + free(cmd); +} + +static const struct xtalk_command_desc *get_command_desc(const struct xtalk_protocol *xproto, uint8_t op) +{ + const struct xtalk_command_desc *desc; + + if(!xproto) + return NULL; + desc = &xproto->commands[op]; + if(!desc->name) + return NULL; +#if 0 + DBG("%s version=%d, op=0x%X (%s)\n", + xproto->name, xproto->proto_version, + op, desc->name); +#endif + return desc; +} + +static const char *ack_status_msg(const struct xtalk_protocol *xproto, uint8_t status) +{ + const char *ack_status; + + if(!xproto) + return NULL; + ack_status = xproto->ack_statuses[status]; + DBG("%s status=0x%X (%s)\n", xproto->name, status, ack_status); + return ack_status; +} + +int xtalk_set_protocol(struct xtalk_device *xtalk_dev, const struct xtalk_protocol *xproto) +{ + const char *protoname = (xproto) ? xproto->name : "GLOBAL"; + int i; + + DBG("%s\n", protoname); + memset(&xtalk_dev->xproto, 0, sizeof(xtalk_dev->xproto)); + for(i = 0; i < MAX_OPS; i++) { + const struct xtalk_command_desc *desc; + + desc = get_command_desc(xproto, i); + if(desc) { + if(!IS_PRIVATE_OP(i)) { + ERR("Bad op=0x%X (should be in the range [0x%X-0x%X]\n", + i, PRIVATE_OP_FIRST, PRIVATE_OP_LAST); + return -EINVAL; + } + xtalk_dev->xproto.commands[i] = *desc; + DBG("private: op=0x%X (%s)\n", i, desc->name); + } else { + if(!IS_PRIVATE_OP(i)) { + const char *name; + + xtalk_dev->xproto.commands[i] = xtalk_base.commands[i]; + name = xtalk_dev->xproto.commands[i].name; + if(name) + DBG("global: op=0x%X (%s)\n", i, name); + } + } + } + for(i = 0; i < MAX_STATUS; i++) { + const char *stat_msg; + + stat_msg = (xproto) ? xproto->ack_statuses[i] : NULL; + if(stat_msg) { + if(!IS_PRIVATE_OP(i)) { + ERR("Bad status=0x%X (should be in the range [0x%X-0x%X]\n", + i, PRIVATE_OP_FIRST, PRIVATE_OP_LAST); + return -EINVAL; + } + xtalk_dev->xproto.ack_statuses[i] = stat_msg; + DBG("private: status=0x%X (%s)\n", i, stat_msg); + } else { + if(!IS_PRIVATE_OP(i)) { + const char *stat_msg; + + xtalk_dev->xproto.ack_statuses[i] = xtalk_base.ack_statuses[i]; + stat_msg = xtalk_dev->xproto.ack_statuses[i]; + if(stat_msg) + DBG("global: status=0x%X (%s)\n", i, stat_msg); + } + } + } + xtalk_dev->xproto.name = protoname; + xtalk_dev->xproto.proto_version = (xproto) ? xproto->proto_version : 0; + return 0; +} + +struct xtalk_command *new_command( + const struct xtalk_device *xtalk_dev, + uint8_t op, uint16_t extra_data) +{ + const struct xtalk_protocol *xproto; + struct xtalk_command *cmd; + const struct xtalk_command_desc *desc; + uint16_t len; + + xproto = &xtalk_dev->xproto; + desc = get_command_desc(xproto, op); + if(!desc) { + ERR("Unknown op=0x%X.\n", op); + return NULL; + } + DBG("OP=0x%X [%s] (extra_data %d)\n", op, desc->name, extra_data); + len = desc->len + extra_data; + if((cmd = malloc(len)) == NULL) { + ERR("Out of memory\n"); + return NULL; + } + if(extra_data) { + uint8_t *ptr = (uint8_t *)cmd; + + DBG("clear extra_data (%d bytes)\n", extra_data); + memset(ptr + desc->len, 0, extra_data); + } + cmd->header.op = op; + cmd->header.len = len; + cmd->header.seq = 0; /* Overwritten in send_usb() */ + return cmd; +} + +void xtalk_dump_command(struct xtalk_command *cmd) +{ + uint16_t len; + int i; + + len = cmd->header.len; + if(len < sizeof(struct xtalk_header)) { + ERR("Command too short (%d)\n", len); + return; + } + INFO("DUMP: OP=0x%X len=%d seq=%d\n", + cmd->header.op, cmd->header.len, cmd->header.seq); + for(i = 0; i < len - sizeof(struct xtalk_header); i++) { + INFO(" %2d. 0x%X\n", i, cmd->alt.raw_data[i]); + } +} + +static int send_command(struct xtalk_device *xtalk_dev, struct xtalk_command *cmd, int timeout) +{ + int ret; + int len; + char *buf; + void *priv = xtalk_dev->transport_priv; + + len = cmd->header.len; + cmd->header.seq = xtalk_dev->tx_sequenceno; + + buf = (char *)cmd; + //printf("%s: len=%d\n", __FUNCTION__, len); +#if 0 + extern FILE *fp; + if(fp) { + int i; + + fprintf(fp, "%05d:", cmd->header.seq); + for(i = 0; i < len; i++) + fprintf(fp, " %02X", (uint8_t)buf[i]); + fprintf(fp, "\n"); + } +#endif + ret = xtalk_dev->ops.send_func(priv, (char *)cmd, len, timeout); + if(ret < 0) { + DBG("send_func failed ret=%d\n", ret); + } + xtalk_dev->tx_sequenceno++; + return ret; +} + +static struct xtalk_command *recv_command(struct xtalk_device *xtalk_dev, int timeout) +{ + struct xtalk_command *reply; + void *priv = xtalk_dev->transport_priv; + int ret; + + if((reply = malloc(xtalk_dev->packet_size)) == NULL) { + ERR("Out of memory\n"); + goto err; + } + reply->header.len = 0; + ret = xtalk_dev->ops.recv_func(priv, (char *)reply, xtalk_dev->packet_size, timeout); + if(ret < 0) { + ERR("Receive from usb failed.\n"); + goto err; + } else if(ret == 0) { + goto err; /* No reply */ + } + if(ret != reply->header.len) { + ERR("Wrong length received: got %d bytes, but length field says %d bytes%s\n", + ret, reply->header.len, + (ret == 1)? ". Old USB firmware?": ""); + goto err; + } + //dump_packet(LOG_DEBUG, DBG_MASK, __FUNCTION__, (char *)reply, ret); + return reply; +err: + if(reply) { + memset(reply, 0, xtalk_dev->packet_size); + free_command(reply); + } + return NULL; +} + + +__attribute__((warn_unused_result)) +int process_command( + struct xtalk_device *xtalk_dev, + struct xtalk_command *cmd, + struct xtalk_command **reply_ref) +{ + const struct xtalk_protocol *xproto; + struct xtalk_command *reply = NULL; + const struct xtalk_command_desc *reply_desc; + const struct xtalk_command_desc *expected; + const struct xtalk_command_desc *cmd_desc; + uint8_t reply_op; + const char *protoname; + int ret; + + xproto = &xtalk_dev->xproto; + protoname = (xproto) ? xproto->name : "GLOBAL"; + if(reply_ref) + *reply_ref = NULL; /* So the caller knows if a reply was received */ + reply_op = cmd->header.op | XTALK_REPLY_MASK; + cmd_desc = get_command_desc(xproto, cmd->header.op); + expected = get_command_desc(xproto, reply_op); + //printf("%s: len=%d\n", __FUNCTION__, cmd->header.len); + ret = send_command(xtalk_dev, cmd, TIMEOUT); + if(!reply_ref) { + DBG("No reply requested\n"); + goto out; + } + if(ret < 0) { + ERR("send_command failed: %d\n", ret); + goto out; + } + reply = recv_command(xtalk_dev, TIMEOUT); + if(!reply) { + ERR("recv_command failed\n"); + ret = -EPROTO; + goto out; + } + *reply_ref = reply; + if((reply->header.op & 0x80) != 0x80) { + ERR("Unexpected reply op=0x%02X, should have MSB set.\n", reply->header.op); + ret = -EPROTO; + goto out; + } + DBG("REPLY OP: 0x%X\n", reply->header.op); + reply_desc = get_command_desc(xproto, reply->header.op); + if(!reply_desc) { + ERR("Unknown reply (proto=%s) op=0x%02X\n", protoname, reply->header.op); + ret = -EPROTO; + goto out; + } + DBG("REPLY NAME: %s\n", reply_desc->name); + if(reply->header.op == XTALK_ACK) { + int status = CMD_FIELD(reply, XTALK, ACK, stat); + + if(expected) { + ERR("Expected OP=0x%02X: Got ACK(%d): %s\n", + reply_op, status, ack_status_msg(xproto, status)); + ret = -EPROTO; + goto out; + } else if(status != STAT_OK) { + + ERR("Got ACK (for OP=0x%X [%s]): %d %s\n", + cmd->header.op, + cmd_desc->name, + status, ack_status_msg(xproto, status)); +#if 0 + extern FILE *fp; + if(fp) { + fprintf(fp, "Got ACK(%d)\n", status); + } +#endif + ret = -EPROTO; + goto out; + } + /* Good expected ACK ... */ + } else if(reply->header.op != reply_op) { + ERR("Expected OP=0x%02X: Got OP=0x%02X\n", + reply_op, reply->header.op); + ret = -EPROTO; + goto out; + } + if(expected && expected->len > reply->header.len) { + ERR("Expected len=%d: Got len=%d\n", + expected->len, reply->header.len); + ret = -EPROTO; + goto out; + } + if(cmd->header.seq != reply->header.seq) { + ERR("Expected seq=%d: Got seq=%d\n", + cmd->header.seq, reply->header.seq); + ret = -EPROTO; + goto out; + } + ret = reply->header.len; /* All good, return the length */ + DBG("returning reply op 0x%X (%d bytes)\n", reply->header.op, ret); +out: + free_command(cmd); + if(!reply_ref && reply) + free_command(reply); + return ret; +} + +/* + * Protocol Commands + */ + +int xtalk_proto_query(struct xtalk_device *xtalk_dev) +{ + struct xtalk_command *cmd; + struct xtalk_command *reply; + uint8_t proto_version; + int ret; + + DBG("\n"); + assert(xtalk_dev != NULL); + proto_version = xtalk_dev->xproto.proto_version; + if((cmd = new_command(xtalk_dev, XTALK_PROTO_GET, 0)) == NULL) { + ERR("new_command failed\n"); + return -ENOMEM; + } + CMD_FIELD(cmd, XTALK, PROTO_GET, proto_version) = proto_version; /* Protocol Version */ + ret = process_command(xtalk_dev, cmd, &reply); + if(ret < 0) { + ERR("process_command failed: %d\n", ret); + goto out; + } + xtalk_dev->xtalk_proto_version = CMD_FIELD(reply, XTALK, PROTO_GET_REPLY, proto_version); + if(xtalk_dev->xtalk_proto_version != proto_version) { + ERR("Got %s protocol version: 0x%02x (expected 0x%02x)\n", + xtalk_dev->xproto.name, + xtalk_dev->xtalk_proto_version, + proto_version); + ret = xtalk_dev->xtalk_proto_version; + goto out; + } + DBG("Protocol version: %02x\n", xtalk_dev->xtalk_proto_version); + ret = xtalk_dev->xtalk_proto_version; +out: + free_command(reply); + return ret; +} + +/* + * Wrappers + */ + +struct xtalk_device *xtalk_new(const struct xtalk_ops *ops, size_t packet_size, void *priv) +{ + struct xtalk_device *xtalk_dev; + int ret; + + DBG("\n"); + assert(ops != NULL); + if((xtalk_dev = malloc(sizeof(*xtalk_dev))) == NULL) { + ERR("Allocating XTALK device memory failed\n"); + return NULL; + } + memset(xtalk_dev, 0, sizeof(*xtalk_dev)); + memcpy((void *)&xtalk_dev->ops, (const void *)ops, sizeof(xtalk_dev->ops)); + xtalk_dev->transport_priv = priv; + xtalk_dev->packet_size = packet_size; + xtalk_dev->tx_sequenceno = 1; + ret = xtalk_set_protocol(xtalk_dev, NULL); + if(ret < 0) { + ERR("GLOBAL Protocol registration failed: %d\n", ret); + goto err; + } + return xtalk_dev; + +err: + if (xtalk_dev) + xtalk_delete(xtalk_dev); + return NULL; +} + +void xtalk_delete(struct xtalk_device *xtalk_dev) +{ + void *priv; + + if(!xtalk_dev) + return; + DBG("\n"); + priv = xtalk_dev->transport_priv; + assert(priv); + xtalk_dev->tx_sequenceno = 0; + assert(&xtalk_dev->ops != NULL); + assert(&xtalk_dev->ops.close_func != NULL); + xtalk_dev->ops.close_func(priv); +} -- cgit v1.2.3