diff options
author | Tzafrir Cohen <tzafrir.cohen@xorcom.com> | 2011-03-10 18:48:11 +0000 |
---|---|---|
committer | Tzafrir Cohen <tzafrir.cohen@xorcom.com> | 2011-03-10 18:48:11 +0000 |
commit | 760d0a2a75aba6307550bf551c5eb06174c256b5 (patch) | |
tree | d526eeb7d5ce0aec9e4572f5df0cb3536a2632d1 /xpp/xtalk | |
parent | cf84710ff24dd09e80f9e5ecfeb16ce15137ddc3 (diff) |
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 <oron.peled@xorcom.com>
Acked-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
git-svn-id: http://svn.asterisk.org/svn/dahdi/tools/trunk@9825 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'xpp/xtalk')
-rw-r--r-- | xpp/xtalk/debug.c | 72 | ||||
-rw-r--r-- | xpp/xtalk/debug.h | 49 | ||||
-rw-r--r-- | xpp/xtalk/xlist.c | 93 | ||||
-rw-r--r-- | xpp/xtalk/xlist.h | 29 | ||||
-rw-r--r-- | xpp/xtalk/xtalk.c | 497 | ||||
-rw-r--r-- | xpp/xtalk/xtalk.h | 172 | ||||
-rw-r--r-- | xpp/xtalk/xtalk_defs.h | 40 | ||||
-rw-r--r-- | xpp/xtalk/xusb.c | 726 | ||||
-rw-r--r-- | xpp/xtalk/xusb.h | 98 |
9 files changed, 1776 insertions, 0 deletions
diff --git a/xpp/xtalk/debug.c b/xpp/xtalk/debug.c new file mode 100644 index 0000000..eea2d82 --- /dev/null +++ b/xpp/xtalk/debug.c @@ -0,0 +1,72 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, 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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdarg.h> +#include <syslog.h> +#include <execinfo.h> +#include <debug.h> + +int verbose = LOG_INFO; +int debug_mask = 0; + +void log_function(int level, int mask, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + if(verbose >= level) { + if(level < LOG_DEBUG || (mask & debug_mask)) + vfprintf(stderr, msg, ap); + } + va_end(ap); +} + +void dump_packet(int loglevel, int mask, const char *msg, const char *buf, int len) +{ + int i; + + if(mask & debug_mask) { + log_function(loglevel, ~0, "%-15s:", msg); + for(i = 0; i < len; i++) + log_function(loglevel, ~0, " %02X", (uint8_t)buf[i]); + log_function(loglevel, ~0, "\n"); + } +} + +/* from glibc info(1) */ +void print_backtrace (FILE *fp) +{ + void *array[10]; + size_t size; + char **strings; + size_t i; + + size = backtrace (array, 10); + strings = backtrace_symbols (array, size); + for (i = 0; i < size; i++) + fprintf (fp, "%s\n", strings[i]); + free (strings); +} diff --git a/xpp/xtalk/debug.h b/xpp/xtalk/debug.h new file mode 100644 index 0000000..2d018d2 --- /dev/null +++ b/xpp/xtalk/debug.h @@ -0,0 +1,49 @@ +#ifndef DEBUG_H +#define DEBUG_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, 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 <syslog.h> +#include <stdio.h> + +/* + * Each module should define a unique DBG_MASK + */ + +extern int verbose; +extern int debug_mask; + +/* + * Logging + */ +void log_function(int level, int mask, const char *msg, ...) __attribute__(( format(printf, 3, 4) )); + +#define ERR(fmt, arg...) log_function(LOG_ERR, 0, "%s:%d: ERROR(%s): " fmt, __FILE__, __LINE__, __FUNCTION__, ## arg) +#define WARN(fmt, arg...) log_function(LOG_WARNING, 0, "WARNING: " fmt, ## arg) +#define INFO(fmt, arg...) log_function(LOG_INFO, 0, "INFO: " fmt, ## arg) +#define DBG(fmt, arg...) log_function(LOG_DEBUG, DBG_MASK, \ + "%s:%d: DBG(%s): " fmt, __FILE__, __LINE__, __FUNCTION__, ## arg) + +void dump_packet(int loglevel, int mask, const char *msg, const char *buf, int len); +void print_backtrace (FILE *fp); + +#endif /* DEBUG_H */ diff --git a/xpp/xtalk/xlist.c b/xpp/xtalk/xlist.c new file mode 100644 index 0000000..d28debd --- /dev/null +++ b/xpp/xtalk/xlist.c @@ -0,0 +1,93 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <xlist.h> + +struct xlist_node *xlist_new(void *data) +{ + struct xlist_node *list; + + if((list = malloc(sizeof(*list))) == NULL) + return NULL; + list->next = list; + list->prev = list; + list->data = data; + return list; +} + +void xlist_destroy(struct xlist_node *list, xlist_destructor_t destructor) +{ + struct xlist_node *curr; + struct xlist_node *next; + + if (! list) + return; + curr = list->next; + while(curr != list) { + next = curr->next; + if(destructor) + destructor(curr->data); + memset(curr, 0, sizeof(*curr)); + free(curr); + curr = next; + } + memset(list, 0, sizeof(*list)); + free(list); +} + +void xlist_append_item(struct xlist_node *list, struct xlist_node *item) +{ + assert(list); + assert(xlist_empty(item)); + item->next = list; + item->prev = list->prev; + list->prev->next = item; + list->prev = item; +} + +void xlist_prepend_item(struct xlist_node *list, struct xlist_node *item) +{ + assert(list); + assert(xlist_empty(item)); + item->prev = list; + item->next = list->next; + list->next->prev = item; + list->next = item; +} + +void xlist_remove_item(struct xlist_node *item) +{ + assert(item); + item->prev->next = item->next; + item->next->prev = item->prev; + item->next = item->prev = item; +} + +struct xlist_node *xlist_shift(struct xlist_node *list) +{ + struct xlist_node *item; + + if(!list) + return NULL; + if(xlist_empty(list)) + return NULL; + item = list->next; + xlist_remove_item(item); + return item; +} + +int xlist_empty(const struct xlist_node *list) +{ + assert(list); + return list->next == list && list->prev == list; +} + +size_t xlist_length(const struct xlist_node *list) +{ + struct xlist_node *curr; + size_t count = 0; + + for(curr = list->next; curr != list; curr = curr->next) + count++; + return count; +} diff --git a/xpp/xtalk/xlist.h b/xpp/xtalk/xlist.h new file mode 100644 index 0000000..4f7f818 --- /dev/null +++ b/xpp/xtalk/xlist.h @@ -0,0 +1,29 @@ +#ifndef XLIST_H +#define XLIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +struct xlist_node { + void *data; + struct xlist_node *next; + struct xlist_node *prev; +}; + +typedef void (*xlist_destructor_t)(void *data); + +struct xlist_node *xlist_new(void *data); +void xlist_destroy(struct xlist_node *list, xlist_destructor_t destructor); +void xlist_append_item(struct xlist_node *list, struct xlist_node *item); +void xlist_remove_item(struct xlist_node *item); +struct xlist_node *xlist_shift(struct xlist_node *list); +int xlist_empty(const struct xlist_node *list); +size_t xlist_length(const struct xlist_node *list); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XLIST_H */ 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 <oron@actcom.co.il> + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <arpa/inet.h> +#include <xtalk.h> +#include <debug.h> + +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); +} diff --git a/xpp/xtalk/xtalk.h b/xpp/xtalk/xtalk.h new file mode 100644 index 0000000..4243b64 --- /dev/null +++ b/xpp/xtalk/xtalk.h @@ -0,0 +1,172 @@ +#ifndef XTALK_H +#define XTALK_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * 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. + * + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * XTALK - Base protocol for our USB devices + * It is meant to provide a common base for layered + * protocols (dialects) + */ + +#include <stdint.h> +#include <stdlib.h> +/* Definitions common to the firmware (in include/ directory) */ +#include <xtalk_defs.h> + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#error "We do not know how your compiler packs structures" +#endif + +struct xtalk_device; +struct xtalk_command_desc; + +typedef int (*xtalk_cmd_callback_t)( + struct xtalk_device *xtalk_dev, + struct xtalk_command_desc *xtalk_cmd); + +/* Describe a single xtalk command */ +struct xtalk_command_desc { + uint8_t op; + const char *name; + xtalk_cmd_callback_t callback; + uint16_t len; /* Minimal length */ +}; + +/* Define a complete protocol */ +struct xtalk_protocol { + const char *name; + uint8_t proto_version; + struct xtalk_command_desc commands[MAX_OPS]; + const char *ack_statuses[MAX_STATUS]; +}; + +/* + * The common header of every xtalk command + * in every xtalk dialect. + */ +struct xtalk_header { + uint16_t len; + uint16_t seq; + uint8_t op; /* MSB: 0 - to device, 1 - from device */ +} PACKED; + +struct xtalk_command { + /* Common part */ + struct xtalk_header header; + /* Each dialect has its own data members */ + union private_data { + uint8_t raw_data[0]; + } PACKED alt; +} PACKED; + +/* + * Macros to unify access to protocol packets and fields: + * p - signify the dialect prefix (XTALK for base protocol) + * o - signify command op (e.g: ACK) + * cmd - A pointer to struct xtalk_command + * field - field name (e.g: raw_data) + */ +#define XTALK_STRUCT(p,o) p ## _struct_ ## o +#define XTALK_PDATA(o) xtalk_privdata_ ## o +#define CMD_FIELD(cmd, p, o, field) (((union XTALK_PDATA(p) *)&((cmd)->alt))->XTALK_STRUCT(p, o).field) +#define CMD_DEF(p, o, ...) struct XTALK_STRUCT(p, o) { \ + __VA_ARGS__ \ + } PACKED XTALK_STRUCT(p, o) +#define MEMBER(p, o) struct XTALK_STRUCT(p, o) XTALK_STRUCT(p, o) + +/* Wrappers for transport (xusb) functions */ +struct xtalk_ops { + int (*send_func)(void *transport_priv, void *data, size_t len, int timeout); + int (*recv_func)(void *transport_priv, void *data, size_t maxlen, int timeout); + int (*close_func)(void *transport_priv); +}; + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_device; + +/* high-level */ +struct xtalk_device *xtalk_new(const struct xtalk_ops *ops, size_t packet_size, void *transport_priv); +void xtalk_delete(struct xtalk_device *dev); +int xtalk_set_protocol(struct xtalk_device *xtalk_dev, const struct xtalk_protocol *xproto); +int xtalk_proto_query(struct xtalk_device *dev); +void xtalk_dump_command(struct xtalk_command *cmd); + +/* low-level */ +int process_command( + struct xtalk_device *dev, + struct xtalk_command *cmd, + struct xtalk_command **reply_ref); +struct xtalk_command *new_command( + const struct xtalk_device *xtalk_dev, + uint8_t op, uint16_t extra_data); +void free_command(struct xtalk_command *cmd); + +/* + * Convenience macros to define entries in a protocol command table: + * p - signify the dialect prefix (XTALK for base protocol) + * o - signify command op (e.g: ACK) + * cb - A callback function (type xtalk_cmd_callback_t) + */ +#define CMD_RECV(p,o,cb) \ + [p ## _ ## o | XTALK_REPLY_MASK] { \ + .op = p ## _ ## o | XTALK_REPLY_MASK, \ + .name = #o "_reply", \ + .callback = (cb), \ + .len = \ + sizeof(struct xtalk_header) + \ + sizeof(struct XTALK_STRUCT(p,o)), \ + } + +#define CMD_SEND(p,o) \ + [p ## _ ## o] { \ + .op = p ## _ ## o, \ + .name = #o, \ + .callback = NULL, \ + .len = \ + sizeof(struct xtalk_header) + \ + sizeof(struct XTALK_STRUCT(p,o)), \ + } + +/* + * Convenience macro to define statuses: + * x - status code (e.g: OK) + * m - status message (const char *) + */ +#define ACK_STAT(x,m) [ STAT_ ## x ] = (m) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XTALK_H */ diff --git a/xpp/xtalk/xtalk_defs.h b/xpp/xtalk/xtalk_defs.h new file mode 100644 index 0000000..d9c590b --- /dev/null +++ b/xpp/xtalk/xtalk_defs.h @@ -0,0 +1,40 @@ +#ifndef XTALK_DEFS_H +#define XTALK_DEFS_H + +#define MAX_OPS 256 /* single byte */ +#define MAX_STATUS 256 /* single byte */ + +#define XTALK_REPLY_MASK 0x80 /* Every reply has this bit */ + +#define PRIVATE_OP_FIRST 0x05 +#define PRIVATE_OP_LAST 0x7F +#define IS_PRIVATE_OP(x) ( \ + (((x) & ~(XTALK_REPLY_MASK)) >= PRIVATE_OP_FIRST) && \ + (((x) & ~(XTALK_REPLY_MASK)) <= PRIVATE_OP_LAST) \ + ) + +#define XTALK_ACK 0x80 +#define XTALK_PROTO_GET 0x01 +#define XTALK_PROTO_GET_REPLY (XTALK_PROTO_GET | XTALK_REPLY_MASK) +#define XTALK_FWVERS_GET 0x11 +#define XTALK_FWVERS_GET_REPLY (XTALK_FWVERS_GET | XTALK_REPLY_MASK) +#define XTALK_CAPS_GET 0x0E /* Get EEPROM table contents Product/Vendor Id ... */ +#define XTALK_CAPS_GET_REPLY (XTALK_CAPS_GET | XTALK_REPLY_MASK) + +/*------------- XTALK: statuses in ACK ---------------------------------------*/ +#define STAT_OK 0x00 /* OK */ +#define STAT_FAIL 0x01 /* last command failed */ +#define STAT_RESET_FAIL 0x02 /* reset failed */ +#define STAT_NODEST 0x03 /* No destination is selected */ +#define STAT_MISMATCH 0x04 /* Data mismatch */ +#define STAT_NOACCESS 0x05 /* No access */ +#define STAT_BAD_CMD 0x06 /* Bad command */ +#define STAT_TOO_SHORT 0x07 /* Packet is too short */ +#define STAT_ERROFFS 0x08 /* Offset error (not used) */ +#define STAT_NO_LEEPROM 0x0A /* Large EEPROM was not found */ +#define STAT_NO_EEPROM 0x0B /* No EEPROM was found */ +#define STAT_WRITE_FAIL 0x0C /* Writing to device failed */ +#define STAT_NOPWR_ERR 0x10 /* No power on USB connector */ + + +#endif /* XTALK_DEFS_H */ diff --git a/xpp/xtalk/xusb.c b/xpp/xtalk/xusb.c new file mode 100644 index 0000000..ebec5a9 --- /dev/null +++ b/xpp/xtalk/xusb.c @@ -0,0 +1,726 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, 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. + * + */ + +#define _GNU_SOURCE /* for memrchr() */ +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <syslog.h> +#include <arpa/inet.h> +#include <debug.h> +#include <xusb.h> + +static const char rcsid[] = "$Id$"; + +#define DBG_MASK 0x01 +#define TIMEOUT 500 + +struct xusb { + struct usb_device *dev; + usb_dev_handle *handle; + const struct xusb_spec *spec; + char iManufacturer[BUFSIZ]; + char iProduct[BUFSIZ]; + char iSerialNumber[BUFSIZ]; + char iInterface[BUFSIZ]; + char devpath_tail[PATH_MAX + 1]; + int bus_num; + int device_num; + int interface_num; + int ep_out; + int ep_in; + int is_usb2; + int is_claimed; + int is_open; + size_t packet_size; +}; + +void xusb_init_spec(struct xusb_spec *spec, char *name, + uint16_t vendor_id, uint16_t product_id, + int nifaces, int iface, int nep, int ep_out, int ep_in) +{ + DBG("Initialize %s: interfaces=%d using interface num=%d endpoints=%d (OUT=0x%2X, IN=0x%2X)\n", + name, nifaces, iface, nep, ep_out, ep_in); + memset(spec, 0, sizeof(*spec)); + spec->name = name; + spec->num_interfaces = nifaces; + spec->my_interface_num = iface; + spec->num_endpoints = nep; + spec->my_vendor_id = vendor_id; + spec->my_product_id = product_id; + spec->my_ep_in = ep_in; + spec->my_ep_out = ep_out; +} + +#define EP_OUT(xusb) ((xusb)->spec->my_ep_out) +#define EP_IN(xusb) ((xusb)->spec->my_ep_in) + +/* + * USB handling + */ + +static int get_usb_string(struct xusb *xusb, uint8_t item, char *buf, unsigned int len) +{ + char tmp[BUFSIZ]; + int ret; + + assert(xusb->handle); + if (!item) + return 0; + ret = usb_get_string_simple(xusb->handle, item, tmp, BUFSIZ); + if (ret <= 0) + return ret; + return snprintf(buf, len, "%s", tmp); +} + +static const struct usb_interface_descriptor *get_interface(const struct usb_device *dev, int my_interface_num, int num_interfaces) +{ + const struct usb_interface *interface; + const struct usb_interface_descriptor *iface_desc; + const struct usb_config_descriptor *config_desc; + int num_altsetting; + + config_desc = dev->config; + if (!config_desc) { + ERR("No configuration descriptor: strange USB1 controller?\n"); + return NULL; + } + if(num_interfaces && config_desc->bNumInterfaces != num_interfaces) { + DBG("Wrong number of interfaces: have %d need %d\n", + config_desc->bNumInterfaces, num_interfaces); + return NULL; + } + interface = &config_desc->interface[my_interface_num]; + assert(interface != NULL); + iface_desc = interface->altsetting; + num_altsetting = interface->num_altsetting; + assert(num_altsetting != 0); + assert(iface_desc != NULL); + return iface_desc; +} + +static int match_interface(const struct usb_device *dev, const struct xusb_spec *spec) +{ + const struct usb_device_descriptor *dev_desc; + const struct usb_interface_descriptor *iface_desc; + + //debug_mask = 0xFF; + //verbose = 1; + dev_desc = &dev->descriptor; + assert(dev_desc); + DBG("Checking: %04X:%04X interfaces=%d interface num=%d endpoints=%d: \"%s\"\n", + spec->my_vendor_id, + spec->my_product_id, + spec->num_interfaces, + spec->my_interface_num, + spec->num_endpoints, + spec->name); + if(dev_desc->idVendor != spec->my_vendor_id) { + DBG("Wrong vendor id 0x%X\n", dev_desc->idVendor); + return 0; + } + if(dev_desc->idProduct != spec->my_product_id) { + DBG("Wrong product id 0x%X\n", dev_desc->idProduct); + return 0; + } + if((iface_desc = get_interface(dev, spec->my_interface_num, spec->num_interfaces)) == NULL) { + ERR("Could not get interface descriptor of device: %s\n", usb_strerror()); + return 0; + } + if(iface_desc->bInterfaceClass != 0xFF) { + DBG("Wrong interface class 0x%X\n", iface_desc->bInterfaceClass); + return 0; + } + if(iface_desc->bInterfaceNumber != spec->my_interface_num) { + DBG("Wrong interface number %d (expected %d)\n", + iface_desc->bInterfaceNumber, spec->my_interface_num); + return 0; + } + if(iface_desc->bNumEndpoints != spec->num_endpoints) { + DBG("Wrong number of endpoints %d\n", iface_desc->bNumEndpoints); + return 0; + } + return 1; +} + +static int xusb_fill_strings(struct xusb *xusb) +{ + const struct usb_device_descriptor *dev_desc; + const struct usb_interface_descriptor *iface_desc; + + + dev_desc = &xusb->dev->descriptor; + assert(dev_desc); + if(get_usb_string(xusb, dev_desc->iManufacturer, xusb->iManufacturer, BUFSIZ) < 0) { + ERR("Failed reading iManufacturer string: %s\n", usb_strerror()); + return 0; + } + if(get_usb_string(xusb, dev_desc->iProduct, xusb->iProduct, BUFSIZ) < 0) { + ERR("Failed reading iProduct string: %s\n", usb_strerror()); + return 0; + } + if(get_usb_string(xusb, dev_desc->iSerialNumber, xusb->iSerialNumber, BUFSIZ) < 0) { + ERR("Failed reading iSerialNumber string: %s\n", usb_strerror()); + return 0; + } + if((iface_desc = get_interface(xusb->dev, xusb->interface_num, 0)) == NULL) { + ERR("Could not get interface descriptor of device: %s\n", usb_strerror()); + return 0; + } + if(get_usb_string(xusb, iface_desc->iInterface, xusb->iInterface, BUFSIZ) < 0) { + ERR("Failed reading iInterface string: %s\n", usb_strerror()); + return 0; + } + return 1; +} + +static int xusb_open(struct xusb *xusb) +{ + assert(xusb); + if (xusb->is_open) + return 1; + if((xusb->handle = usb_open(xusb->dev)) == NULL) { + ERR("Failed to open usb device '%s': %s\n", + xusb->devpath_tail, usb_strerror()); + return 0; + } + xusb->is_open = 1; + return 1; +} + +int xusb_claim_interface(struct xusb *xusb) +{ + const struct usb_device_descriptor *dev_desc; + int ret; + + assert(xusb); + xusb_open(xusb); /* If it's not open yet... */ + if(usb_claim_interface(xusb->handle, xusb->interface_num) != 0) { + ERR("usb_claim_interface %d in '%s': %s\n", + xusb->interface_num, xusb->devpath_tail, usb_strerror()); + return 0; + } + xusb->is_claimed = 1; + xusb_fill_strings(xusb); + dev_desc = &xusb->dev->descriptor; + DBG("ID=%04X:%04X Manufacturer=[%s] Product=[%s] SerialNumber=[%s] Interface=[%s]\n", + dev_desc->idVendor, + dev_desc->idProduct, + xusb->iManufacturer, + xusb->iProduct, + xusb->iSerialNumber, + xusb->iInterface); + if(usb_clear_halt(xusb->handle, EP_OUT(xusb)) != 0) { + ERR("Clearing output endpoint: %s\n", usb_strerror()); + return 0; + } + if(usb_clear_halt(xusb->handle, EP_IN(xusb)) != 0) { + ERR("Clearing input endpoint: %s\n", usb_strerror()); + return 0; + } + if((ret = xusb_flushread(xusb)) < 0) { + ERR("xusb_flushread failed: %d\n", ret); + return 0; + } + return 1; +} + +static void xusb_list_dump(struct xlist_node *xusb_list) +{ + struct xlist_node *curr; + struct xusb *xusb; + + for(curr = xusb_list->next; curr != xusb_list; curr = curr->next) { + struct usb_device *dev; + struct usb_bus *bus; + struct usb_device_descriptor *dev_desc; + + xusb = curr->data; + assert(xusb); + dev = xusb->dev; + assert(dev); + bus = dev->bus; + assert(bus); + dev_desc = &dev->descriptor; + assert(dev_desc); + DBG("usb:ID=%04X:%04X [%s / %s / %s], (%s/%s)\n", + dev_desc->idVendor, + dev_desc->idProduct, + xusb->iManufacturer, + xusb->iProduct, + xusb->iSerialNumber, + bus->dirname, + dev->filename + ); + } +} + +void xusb_destroy(struct xusb *xusb) +{ + if(xusb) { + xusb_close(xusb); + memset(xusb, 0, sizeof(*xusb)); + free(xusb); + } +} + +static struct xusb *xusb_new(struct usb_device *dev, const struct xusb_spec *spec) +{ + struct usb_device_descriptor *dev_desc; + struct usb_config_descriptor *config_desc; + struct usb_interface *interface; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t max_packet_size; + int i; + struct xusb *xusb = NULL; + + /* + * Get information from the usb_device + */ + if((dev_desc = &dev->descriptor) == NULL) { + ERR("usb device without a device descriptor\n"); + goto fail; + } + if((config_desc = dev->config) == NULL) { + ERR("usb device without a configuration descriptor\n"); + goto fail; + } + interface = &config_desc->interface[spec->my_interface_num]; + iface_desc = interface->altsetting; + endpoint = iface_desc->endpoint; + /* Calculate max packet size */ + max_packet_size = PACKET_SIZE; + for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) { + DBG("Validating endpoint @ %d (interface %d)\n", i, spec->my_interface_num); + if(endpoint->bEndpointAddress == spec->my_ep_out || endpoint->bEndpointAddress == spec->my_ep_in) { + if(endpoint->wMaxPacketSize > PACKET_SIZE) { + ERR("Endpoint #%d wMaxPacketSize too large (%d)\n", i, endpoint->wMaxPacketSize); + goto fail; + } + if(endpoint->wMaxPacketSize < max_packet_size) { + max_packet_size = endpoint->wMaxPacketSize; + } + } + } + /* Fill xusb */ + if((xusb = malloc(sizeof(*xusb))) == NULL) { + ERR("Out of memory"); + goto fail; + } + memset(xusb, 0, sizeof(*xusb)); + xusb->dev = dev; + xusb->spec = spec; + sscanf(dev->bus->dirname, "%d", &xusb->bus_num); + sscanf(dev->filename, "%d", &xusb->device_num); + snprintf(xusb->devpath_tail, PATH_MAX, "%03d/%03d", + xusb->bus_num, xusb->device_num); + xusb->interface_num = spec->my_interface_num; + xusb->ep_out = spec->my_ep_out; + xusb->ep_in = spec->my_ep_in; + xusb->packet_size = max_packet_size; + xusb->is_usb2 = (max_packet_size == 512); + if (! xusb_open(xusb)) { + ERR("Failed opening device: %04X:%04X - %s\n", + dev_desc->idVendor, + dev_desc->idProduct, + xusb->devpath_tail); + goto fail; + } + DBG("%04X:%04X - %s\n", + dev_desc->idVendor, + dev_desc->idProduct, + xusb->devpath_tail); + return xusb; +fail: + xusb_destroy(xusb); + return NULL; +} + +struct xusb *xusb_find_iface(const char *devpath, int iface_num, int ep_out, int ep_in) +{ + struct usb_bus *bus; + + DBG("\n"); + usb_init(); + usb_find_busses(); + usb_find_devices(); + for (bus = usb_get_busses(); bus; bus = bus->next) { + int bus_num; + char tmppath[PATH_MAX + 1]; + struct usb_device *dev; + + tmppath[0] = '\0'; + sscanf(bus->dirname, "%d", &bus_num); + snprintf(tmppath, sizeof(tmppath), "%03d", bus_num); + DBG("Check bus %d: %s ? %s\n", bus_num, tmppath, devpath); + if (strncmp(tmppath, devpath, strlen(tmppath)) != 0) + continue; + DBG("Matched bus %d\n", bus_num); + for (dev = bus->devices; dev; dev = dev->next) { + struct usb_device_descriptor *dev_desc; + struct usb_config_descriptor *config_desc; + struct usb_interface *interface; + struct xusb_spec spec; + struct xusb *xusb; + int device_num; + + sscanf(dev->filename, "%d", &device_num); + DBG("Check device %d\n", device_num); + snprintf(tmppath, sizeof(tmppath), "%03d/%03d", bus_num, device_num); + if (strncmp(tmppath, devpath, strlen(tmppath)) != 0) + continue; + dev_desc = &dev->descriptor; + assert(dev_desc); + config_desc = dev->config; + assert(config_desc); + interface = config_desc->interface; + assert(interface); + INFO("Matched device %s: %X:%X\n", tmppath, dev_desc->idVendor, dev_desc->idProduct); + xusb_init_spec(&spec, "Astribank", + dev_desc->idVendor, dev_desc->idProduct, + config_desc->bNumInterfaces, + iface_num, + interface->altsetting->bNumEndpoints, + ep_out, ep_in); + if((xusb = xusb_new(dev, &spec)) == NULL) { + ERR("xusb allocation failed\n"); + } + return xusb; + } + } + return NULL; +} + +static const char *path_tail(const char *path) +{ + const char *p; + + assert(path != NULL); + /* Find last '/' */ + if((p = memrchr(path, '/', strlen(path))) == NULL) { + ERR("Missing a '/' in %s\n", path); + return NULL; + } + /* Search for a '/' before that */ + if((p = memrchr(path, '/', p - path)) == NULL) { + p = path; /* No more '/' */ + } else { + p++; /* skip '/' */ + } + return p; +} + +int xusb_filter_bypath(const struct xusb *xusb, void *data) +{ + const char *p; + const char *path = data; + + DBG("%s\n", path); + assert(path != NULL); + p = path_tail(path); + if(strcmp(xusb->devpath_tail, p) != 0) { + DBG("device path missmatch: '%s' != '%s'\n", xusb->devpath_tail, p); + return 0; + } + return 1; +} + +struct xusb *xusb_find_bypath(const struct xusb_spec *specs, int numspecs, const char *path) +{ + struct xlist_node *xlist; + struct xlist_node *head; + struct xusb *xusb; + + xlist = xusb_find_byproduct(specs, numspecs, xusb_filter_bypath, (void *)path); + head = xlist_shift(xlist); + if (!head) + return NULL; + if (! xlist_empty(xlist)) { + ERR("Too many matches (extra %zd) to '%s'\n", xlist_length(xlist), path); + return NULL; + } + xusb = head->data; + xlist_destroy(xlist, NULL); + return xusb; +} + +struct xlist_node *xusb_find_byproduct(const struct xusb_spec *specs, int numspecs, xusb_filter_t filterfunc, void *data) +{ + struct xlist_node *xlist; + struct usb_bus *bus; + struct usb_device *dev; + + DBG("specs(%d)\n", numspecs); + if((xlist = xlist_new(NULL)) == NULL) { + ERR("Failed allocation new xlist"); + goto fail_xlist; + } + usb_init(); + usb_find_busses(); + usb_find_devices(); + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + struct usb_device_descriptor *dev_desc; + struct xlist_node *item; + int i; + + dev_desc = &dev->descriptor; + assert(dev_desc); + DBG("usb:%s/%s: ID=%04X:%04X\n", + dev->bus->dirname, + dev->filename, + dev_desc->idVendor, + dev_desc->idProduct); + for(i = 0; i < numspecs; i++) { + struct xusb *xusb; + const struct xusb_spec *sp = &specs[i]; + + if(!match_interface(dev, sp)) + continue; + if((xusb = xusb_new(dev, sp)) == NULL) { + ERR("xusb allocation failed\n"); + goto fail_malloc; + } + if(filterfunc && !filterfunc(xusb, data)) { + xusb_destroy(xusb); + continue; + } + item = xlist_new(xusb); + xlist_append_item(xlist, item); + break; + } + } + } + xusb_list_dump(xlist); + return xlist; +fail_malloc: + xlist_destroy(xlist, NULL); +fail_xlist: + return NULL; +} + +struct xusb *xusb_open_one(const struct xusb_spec *specs, int numspecs, xusb_filter_t filterfunc, void *data) +{ + struct xlist_node *xusb_list; + struct xlist_node *curr; + int num; + struct xusb *xusb = NULL; + + xusb_list = xusb_find_byproduct(specs, numspecs, filterfunc, data); + num = xlist_length(xusb_list); + DBG("total %d devices\n", num); + switch(num) { + case 0: + ERR("No matching device.\n"); + break; + case 1: + curr = xlist_shift(xusb_list); + xusb = curr->data; + xlist_destroy(curr, NULL); + xlist_destroy(xusb_list, NULL); + xusb_claim_interface(xusb); + xusb_showinfo(xusb); + break; + default: + ERR("Too many devices (%d). Aborting.\n", num); + break; + } + return xusb; +} + +int xusb_interface(struct xusb *xusb) +{ + return xusb->interface_num; +} + +size_t xusb_packet_size(const struct xusb *xusb) +{ + return xusb->packet_size; +} + +/* + * MP device handling + */ +void xusb_showinfo(const struct xusb *xusb) +{ + struct usb_device_descriptor *dev_desc; + struct usb_device *dev; + + assert(xusb != NULL); + dev = xusb->dev; + dev_desc = &dev->descriptor; + if(verbose <= LOG_INFO) { + INFO("usb:%s/%s: ID=%04X:%04X [%s / %s / %s]\n", + dev->bus->dirname, + dev->filename, + dev_desc->idVendor, + dev_desc->idProduct, + xusb->iManufacturer, + xusb->iProduct, + xusb->iSerialNumber); + } else { + printf("USB Bus/Device: [%s/%s] (%s,%s)\n", + dev->bus->dirname, + dev->filename, + (xusb->is_open) ? "open" : "closed", + (xusb->is_claimed) ? "claimed" : "unused"); + printf("USB Spec name: [%s]\n", xusb->spec->name); + printf("USB iManufacturer: [%s]\n", xusb->iManufacturer); + printf("USB iProduct: [%s]\n", xusb->iProduct); + printf("USB iSerialNumber: [%s]\n", xusb->iSerialNumber); + } +} + +const char *xusb_serial(const struct xusb *xusb) +{ + return xusb->iSerialNumber; +} + +const char *xusb_devpath(const struct xusb *xusb) +{ + return xusb->devpath_tail; +} + +const char *xusb_manufacturer(const struct xusb *xusb) +{ + return xusb->iManufacturer; +} + +const char *xusb_product(const struct xusb *xusb) +{ + return xusb->iProduct; +} + +uint16_t xusb_vendor_id(const struct xusb *xusb) +{ + return xusb->dev->descriptor.idVendor; +} + +uint16_t xusb_product_id(const struct xusb *xusb) +{ + return xusb->dev->descriptor.idProduct; +} + +const struct xusb_spec *xusb_spec(const struct xusb *xusb) +{ + return xusb->spec; +} + +int xusb_close(struct xusb *xusb) +{ + if(xusb) { + if(xusb->handle) { + assert(xusb->spec); + assert(xusb->spec->name); + DBG("Closing interface \"%s\"\n", xusb->spec->name); + if(xusb->is_claimed) { + if(usb_release_interface(xusb->handle, xusb->spec->my_interface_num) != 0) { + ERR("Releasing interface: usb: %s\n", usb_strerror()); + } + xusb->is_claimed = 0; + } + if(xusb->is_open) { + if(usb_close(xusb->handle) != 0) { + ERR("Closing device: usb: %s\n", usb_strerror()); + } + xusb->is_open = 0; + } + xusb->handle = NULL; + } + xusb = NULL; + } + return 0; +} + +int xusb_send(struct xusb *xusb, char *buf, int len, int timeout) +{ + int ret; + + dump_packet(LOG_DEBUG, DBG_MASK, __FUNCTION__, buf, len); + if(EP_OUT(xusb) & USB_ENDPOINT_IN) { + ERR("%s called with an input endpoint 0x%x\n", __FUNCTION__, EP_OUT(xusb)); + return -EINVAL; + } + ret = usb_bulk_write(xusb->handle, EP_OUT(xusb), buf, len, timeout); + if(ret < 0) { + /* + * If the device was gone, it may be the + * result of renumeration. Ignore it. + */ + if(ret != -ENODEV) { + ERR("bulk_write to endpoint 0x%x failed: (%d) %s\n", + EP_OUT(xusb), ret, usb_strerror()); + dump_packet(LOG_ERR, DBG_MASK, "xbus_send[ERR]", buf, len); + //exit(2); + } else { + DBG("bulk_write to endpoint 0x%x got ENODEV\n", EP_OUT(xusb)); + xusb_close(xusb); + } + return ret; + } else if(ret != len) { + ERR("bulk_write to endpoint 0x%x short write: (%d) %s\n", + EP_OUT(xusb), ret, usb_strerror()); + dump_packet(LOG_ERR, DBG_MASK, "xbus_send[ERR]", buf, len); + return -EFAULT; + } + return ret; +} + +int xusb_recv(struct xusb *xusb, char *buf, size_t len, int timeout) +{ + int ret; + + if(EP_IN(xusb) & USB_ENDPOINT_OUT) { + ERR("%s called with an output endpoint 0x%x\n", __FUNCTION__, EP_IN(xusb)); + return -EINVAL; + } + ret = usb_bulk_read(xusb->handle, EP_IN(xusb), buf, len, timeout); + if(ret < 0) { + DBG("bulk_read from endpoint 0x%x failed: (%d) %s\n", + EP_IN(xusb), ret, usb_strerror()); + memset(buf, 0, len); + return ret; + } + dump_packet(LOG_DEBUG, DBG_MASK, __FUNCTION__, buf, ret); + return ret; +} + +int xusb_flushread(struct xusb *xusb) +{ + char tmpbuf[BUFSIZ]; + int ret; + + DBG("starting...\n"); + memset(tmpbuf, 0, BUFSIZ); + ret = xusb_recv(xusb, tmpbuf, BUFSIZ, 1); + if(ret < 0 && ret != -ETIMEDOUT) { + ERR("ret=%d\n", ret); + return ret; + } else if(ret > 0) { + DBG("Got %d bytes:\n", ret); + dump_packet(LOG_DEBUG, DBG_MASK, __FUNCTION__, tmpbuf, ret); + } + return 0; +} diff --git a/xpp/xtalk/xusb.h b/xpp/xtalk/xusb.h new file mode 100644 index 0000000..65da029 --- /dev/null +++ b/xpp/xtalk/xusb.h @@ -0,0 +1,98 @@ +#ifndef XUSB_H +#define XUSB_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, 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 <stdio.h> +#include <stdint.h> +#include <usb.h> +#include <xlist.h> + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * Xorcom usb handling + */ + +#define PACKET_SIZE 512 + +/* + * Specify the wanted interface + */ +struct xusb_spec { + /* Sanity checks so we know it is our device indeed */ + int num_interfaces; + int num_endpoints; + char *name; /* For debug/output purpose */ + /* What we will actually use */ + uint16_t my_vendor_id; + uint16_t my_product_id; + int my_interface_num; + int my_ep_out; + int my_ep_in; +}; + +void xusb_init_spec(struct xusb_spec *xusb_spec, char *name, + uint16_t vendor_id, uint16_t product_id, + int nifaces, int iface, int nep, int ep_out, int ep_in); + +struct xusb; + +/* + * Prototypes + */ +typedef int (*xusb_filter_t)(const struct xusb *xusb, void *data); +struct xlist_node *xusb_find_byproduct(const struct xusb_spec *specs, int numspecs, xusb_filter_t filterfunc, void *data); +struct xusb *xusb_find_bypath(const struct xusb_spec *specs, int numspecs, const char *path); +struct xusb *xusb_open_one(const struct xusb_spec *specs, int numspecs, xusb_filter_t filterfunc, void *data); +struct xusb *xusb_find_iface(const char *devpath, int iface_num, int ep_out, int ep_in); + +/* + * A convenience filter + */ +int xusb_filter_bypath(const struct xusb *xusb, void *data); + +int xusb_interface(struct xusb *xusb); +int xusb_claim_interface(struct xusb *xusb); +void xusb_destroy(struct xusb *xusb); +int xusb_close(struct xusb *xusb); +size_t xusb_packet_size(const struct xusb *xusb); +void xusb_showinfo(const struct xusb *xusb); +const char *xusb_serial(const struct xusb *xusb); +const char *xusb_manufacturer(const struct xusb *xusb); +const char *xusb_product(const struct xusb *xusb); +uint16_t xusb_vendor_id(const struct xusb *xusb); +uint16_t xusb_product_id(const struct xusb *xusb); +const char *xusb_devpath(const struct xusb *xusb); +const struct xusb_spec *xusb_spec(const struct xusb *xusb); +int xusb_send(struct xusb *xusb, char *buf, int len, int timeout); +int xusb_recv(struct xusb *xusb, char *buf, size_t len, int timeout); +int xusb_flushread(struct xusb *xusb); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XUSB_H */ |