From 9c61e40187a6ad8a7d76432dd479336435cc388b Mon Sep 17 00:00:00 2001 From: Oron Peled Date: Sun, 21 Dec 2014 10:39:19 -0500 Subject: [PATCH] xpp: add all base libxtalk files * Do not remove yet the old ones. * So new files are built, but not used yet. * Build as a "noinst_" convenience library. * Also, compile with '-Wno-unknown-pragmas' because we use some pragmas. Signed-off-by: Oron Peled Signed-off-by: Tzafrir Cohen --- .gitignore | 6 + configure.ac | 1 + xpp/Makefile.am | 2 +- xpp/xtalk/Makefile.am | 84 +++ xpp/xtalk/debug.c | 3 +- xpp/xtalk/include/xtalk/api_defs.h | 38 ++ xpp/xtalk/include/xtalk/debug.h | 53 ++ xpp/xtalk/include/xtalk/firmware_defs.h | 40 ++ xpp/xtalk/include/xtalk/proto.h | 186 ++++++ xpp/xtalk/include/xtalk/proto_raw.h | 76 +++ xpp/xtalk/include/xtalk/proto_sync.h | 75 +++ xpp/xtalk/include/xtalk/xlist.h | 32 + xpp/xtalk/include/xtalk/xtalk_iface.h | 19 + xpp/xtalk/include/xtalk/xusb.h | 115 ++++ xpp/xtalk/include/xtalk/xusb_iface.h | 43 ++ xpp/xtalk/xlist.c | 14 +- xpp/xtalk/xlist_test.c | 73 ++ xpp/xtalk/xtalk-xusb.c | 70 ++ xpp/xtalk/xtalk_base.c | 353 ++++++++++ xpp/xtalk/xtalk_base.h | 73 ++ xpp/xtalk/xtalk_raw.c | 221 +++++++ xpp/xtalk/xtalk_raw_test.c | 207 ++++++ xpp/xtalk/xtalk_send.8 | 49 ++ xpp/xtalk/xtalk_send.c | 220 +++++++ xpp/xtalk/xtalk_sync.c | 290 ++++++++ xpp/xtalk/xtalk_test.c | 199 ++++++ xpp/xtalk/xusb_common.c | 381 +++++++++++ xpp/xtalk/xusb_common.h | 104 +++ xpp/xtalk/xusb_libusb.c | 843 ++++++++++++++++++++++++ xpp/xtalk/xusb_libusbx.c | 826 +++++++++++++++++++++++ xpp/xtalk/xusb_test.c | 124 ++++ xpp/xtalk/xusb_test_bypath.c | 76 +++ 32 files changed, 4893 insertions(+), 3 deletions(-) create mode 100644 xpp/xtalk/Makefile.am create mode 100644 xpp/xtalk/include/xtalk/api_defs.h create mode 100644 xpp/xtalk/include/xtalk/debug.h create mode 100644 xpp/xtalk/include/xtalk/firmware_defs.h create mode 100644 xpp/xtalk/include/xtalk/proto.h create mode 100644 xpp/xtalk/include/xtalk/proto_raw.h create mode 100644 xpp/xtalk/include/xtalk/proto_sync.h create mode 100644 xpp/xtalk/include/xtalk/xlist.h create mode 100644 xpp/xtalk/include/xtalk/xtalk_iface.h create mode 100644 xpp/xtalk/include/xtalk/xusb.h create mode 100644 xpp/xtalk/include/xtalk/xusb_iface.h create mode 100644 xpp/xtalk/xlist_test.c create mode 100644 xpp/xtalk/xtalk-xusb.c create mode 100644 xpp/xtalk/xtalk_base.c create mode 100644 xpp/xtalk/xtalk_base.h create mode 100644 xpp/xtalk/xtalk_raw.c create mode 100644 xpp/xtalk/xtalk_raw_test.c create mode 100644 xpp/xtalk/xtalk_send.8 create mode 100644 xpp/xtalk/xtalk_send.c create mode 100644 xpp/xtalk/xtalk_sync.c create mode 100644 xpp/xtalk/xtalk_test.c create mode 100644 xpp/xtalk/xusb_common.c create mode 100644 xpp/xtalk/xusb_common.h create mode 100644 xpp/xtalk/xusb_libusb.c create mode 100644 xpp/xtalk/xusb_libusbx.c create mode 100644 xpp/xtalk/xusb_test.c create mode 100644 xpp/xtalk/xusb_test_bypath.c diff --git a/.gitignore b/.gitignore index 3e9d0cc..87e8496 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,10 @@ xpp/test_parse xpp/twinstar.8 xpp/xpp_blink.8 xpp/xpp_sync.8 +xpp/xtalk/xlist_test +xpp/xtalk/xtalk_raw_test +xpp/xtalk/xtalk_send +xpp/xtalk/xtalk_test +xpp/xtalk/xusb_test +xpp/xtalk/xusb_test_bypath zonedata.lo diff --git a/configure.ac b/configure.ac index 38a1d12..74f1f0b 100644 --- a/configure.ac +++ b/configure.ac @@ -372,6 +372,7 @@ AC_CONFIG_FILES([ hotplug/Makefile ppp/Makefile xpp/Makefile + xpp/xtalk/Makefile xpp/oct612x/Makefile xpp/perl_modules/Makefile ]) diff --git a/xpp/Makefile.am b/xpp/Makefile.am index 0168036..e6fb290 100644 --- a/xpp/Makefile.am +++ b/xpp/Makefile.am @@ -64,7 +64,7 @@ CLEANFILES = $(perl_checks) $(perl_mans) if PBX_USB -SUBDIRS += oct612x +SUBDIRS += oct612x xtalk if LIBUSBX USB_CFLAGS = $(LIBUSBX_CFLAGS) diff --git a/xpp/xtalk/Makefile.am b/xpp/xtalk/Makefile.am new file mode 100644 index 0000000..e26e6b1 --- /dev/null +++ b/xpp/xtalk/Makefile.am @@ -0,0 +1,84 @@ +VISIBILITY_DEFS = -DXTALK_DLL -DXTALK_DLL_EXPORTS +COMMON_CFLAGS = -Wall -Wno-unknown-pragmas -Werror $(VISIBILITY_DEFS) $(CFLAG_VISIBILITY) +AM_CFLAGS = $(COMMON_CFLAGS) + + +if LIBUSBX +USB_CFLAGS = $(LIBUSBX_CFLAGS) +USB_LIBS = $(LIBUSBX_LIBS) +USB_NAME = libusbx +else +if LIBUSB +USB_CFLAGS = $(LIBUSB_CFLAGS) +USB_LIBS = $(LIBUSB_LIBS) +USB_NAME = libusb +endif +endif + +noinst_PROGRAMS = xlist_test xusb_test xusb_test_bypath xtalk_test xtalk_raw_test +sbin_PROGRAMS = xtalk_send +noinst_LTLIBRARIES = libxtalk.la +dist_noinst_HEADERS = \ + debug.h \ + xlist.h \ + xtalk.h \ + xtalk_base.h \ + xtalk_defs.h \ + xusb.h \ + xusb_common.h \ + include/xtalk/proto_raw.h \ + include/xtalk/api_defs.h \ + include/xtalk/xlist.h \ + include/xtalk/proto_sync.h \ + include/xtalk/xusb_iface.h \ + include/xtalk/proto.h \ + include/xtalk/debug.h \ + include/xtalk/xusb.h \ + include/xtalk/firmware_defs.h \ + include/xtalk/xtalk_iface.h \ + # + +man_MANS = xtalk_send.8 + +libxtalk_la_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) $(USB_CFLAGS) +libxtalk_la_LDFLAGS = # +libxtalk_la_LIBADD = $(USB_LIBS) +libxtalk_la_SOURCES = \ + $(dist_noinst_HEADERS) \ + xtalk_sync.c \ + xtalk_raw.c \ + xtalk_base.c \ + xlist.c \ + debug.c \ + xtalk-xusb.c \ + xusb_common.c +if LIBUSBX +libxtalk_la_SOURCES += xusb_libusbx.c +else +if LIBUSB +libxtalk_la_SOURCES += xusb_libusb.c +endif +endif +libxtalk_la_DEPENDENCIES = $(libxtalk_la_SOURCES) + +xtalk_send_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) +xtalk_send_LDADD = libxtalk.la $(USB_LIBS) + +xtalk_test_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) +xtalk_test_LDADD = libxtalk.la $(USB_LIBS) + +xtalk_raw_test_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) +xtalk_raw_test_LDADD = libxtalk.la $(USB_LIBS) + +xusb_test_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) +xusb_test_LDADD = libxtalk.la $(USB_LIBS) + +xusb_test_bypath_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) +xusb_test_bypath_LDADD = libxtalk.la $(USB_LIBS) + +xlist_test_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/include -I$(srcdir) +xlist_test_LDADD = libxtalk.la $(USB_LIBS) + +DISTCLEANFILES = xtalk.pc xtalk-uninstalled.pc + +EXTRA_DIST = $(man_MANS) diff --git a/xpp/xtalk/debug.c b/xpp/xtalk/debug.c index d2d4e15..88eace4 100644 --- a/xpp/xtalk/debug.c +++ b/xpp/xtalk/debug.c @@ -27,7 +27,8 @@ #include #include #include -#include +#include +#include int verbose = LOG_INFO; int debug_mask; diff --git a/xpp/xtalk/include/xtalk/api_defs.h b/xpp/xtalk/include/xtalk/api_defs.h new file mode 100644 index 0000000..4188754 --- /dev/null +++ b/xpp/xtalk/include/xtalk/api_defs.h @@ -0,0 +1,38 @@ +#ifndef XTALK_API_DEFS_H +#define XTALK_API_DEFS_H + +/* + * Visibility settings: taken from: + * http://gcc.gnu.org/wiki/Visibility + */ + +/* Generic helper definitions for shared library support */ +#if __GNUC__ >= 4 + #define XTALK_HELPER_DLL_IMPORT __attribute__ ((visibility ("default"))) + #define XTALK_HELPER_DLL_EXPORT __attribute__ ((visibility ("default"))) + #define XTALK_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden"))) +#else + #define XTALK_HELPER_DLL_IMPORT + #define XTALK_HELPER_DLL_EXPORT + #define XTALK_HELPER_DLL_LOCAL +#endif + +/* + * Now we use the generic helper definitions above to define XTALK_API and XTALK_LOCAL. + * XTALK_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build) + * XTALK_LOCAL is used for non-api symbols. + */ + +#ifdef XTALK_DLL /* defined if XTALK is compiled as a DLL */ + #ifdef XTALK_DLL_EXPORTS /* defined if we are building the XTALK DLL (instead of using it) */ + #define XTALK_API XTALK_HELPER_DLL_EXPORT + #else + #define XTALK_API XTALK_HELPER_DLL_IMPORT + #endif /* XTALK_DLL_EXPORTS */ + #define XTALK_LOCAL XTALK_HELPER_DLL_LOCAL +#else /* XTALK_DLL is not defined: this means XTALK is a static lib. */ + #define XTALK_API + #define XTALK_LOCAL +#endif /* XTALK_DLL */ + +#endif /* XTALK_API_DEFS_H */ diff --git a/xpp/xtalk/include/xtalk/debug.h b/xpp/xtalk/include/xtalk/debug.h new file mode 100644 index 0000000..b7f3226 --- /dev/null +++ b/xpp/xtalk/include/xtalk/debug.h @@ -0,0 +1,53 @@ +#ifndef DEBUG_H +#define DEBUG_H +/* + * Written by Oron Peled + * 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 +#include +#include + +/* + * Each module should define a unique DBG_MASK + */ + +XTALK_API extern int verbose; +XTALK_API extern int debug_mask; + +/* + * Logging + */ +XTALK_API 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__, __func__, ## 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__, __func__, ## arg) + +XTALK_API void dump_packet(int loglevel, int mask, const char *msg, + const char *buf, int len); +XTALK_API void print_backtrace(FILE *fp); + +#endif /* DEBUG_H */ diff --git a/xpp/xtalk/include/xtalk/firmware_defs.h b/xpp/xtalk/include/xtalk/firmware_defs.h new file mode 100644 index 0000000..af2072e --- /dev/null +++ b/xpp/xtalk/include/xtalk/firmware_defs.h @@ -0,0 +1,40 @@ +#ifndef XTALK_FIRMWARE_DEFS_H +#define XTALK_FIRMWARE_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_FIRMWARE_DEFS_H */ diff --git a/xpp/xtalk/include/xtalk/proto.h b/xpp/xtalk/include/xtalk/proto.h new file mode 100644 index 0000000..946ad6a --- /dev/null +++ b/xpp/xtalk/include/xtalk/proto.h @@ -0,0 +1,186 @@ +#ifndef XTALK_PROTO_H +#define XTALK_PROTO_H +/* + * 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. + * + */ + +#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 +#include +#include +#include + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#error "We do not know how your compiler packs structures" +#endif + +struct xtalk_base; +struct xtalk_command_desc; +struct xtalk_command; + +/* + * Callbacks should return negative errno's + * in case of errors. + * They are called from process_command() and their + * return values are propagated back. + */ +typedef int (*xtalk_cmd_callback_t)( + const struct xtalk_base *xtalk_base, + const struct xtalk_command_desc *cmd_desc, + struct xtalk_command *cmd); + +/* Describe a single xtalk command */ +struct xtalk_command_desc { + uint8_t op; + const char *name; + 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 XTALK_CMD_PTR(cmd, p) ((union XTALK_PDATA(p)*)&((cmd)->alt)) +#define CMD_FIELD(cmd, p, o, field) \ + (XTALK_CMD_PTR(cmd, p)->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) +#define XTALK_OP(p, o) (p ## _ ## o) + +/* Wrappers for transport (xusb) functions */ +struct xtalk_ops { + int (*send_func)(void *transport_priv, const char *data, size_t len, + int timeout); + int (*recv_func)(void *transport_priv, char *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_base; +struct xusb_iface; + +/* high-level */ +XTALK_API struct xtalk_base *xtalk_base_new_on_xusb(struct xusb_iface *xusb_iface); +XTALK_API struct xtalk_base *xtalk_base_new(const struct xtalk_ops *ops, + size_t packet_size, void *priv); +XTALK_API void xtalk_base_delete(struct xtalk_base *xtalk_base); +XTALK_API struct xusb_iface *xusb_iface_of_xtalk_base(const struct xtalk_base *xtalk_base); +XTALK_API const char *xtalk_protocol_name(const struct xtalk_base *dev); +XTALK_API int xtalk_cmd_callback(struct xtalk_base *xtalk_base, int op, + xtalk_cmd_callback_t callback, + xtalk_cmd_callback_t *old_callback); +XTALK_API void xtalk_dump_command(struct xtalk_command *cmd); +XTALK_API int xtalk_set_timeout(struct xtalk_base *dev, int new_timeout); + +/* low-level */ +XTALK_API const char *ack_status_msg(const struct xtalk_protocol *xproto, + uint8_t status); +XTALK_API struct xtalk_command *new_command( + const struct xtalk_base *xtalk_base, + uint8_t op, uint16_t extra_data); +XTALK_API 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) + */ +#define CMD_RECV(p, o) \ + [p ## _ ## o | XTALK_REPLY_MASK] = { \ + .op = (p ## _ ## o) | XTALK_REPLY_MASK, \ + .name = (#o "_reply"), \ + .len = \ + sizeof(struct xtalk_header) + \ + sizeof(struct XTALK_STRUCT(p, o)), \ + } + +#define CMD_SEND(p, o) \ + [p ## _ ## o] = { \ + .op = (p ## _ ## o), \ + .name = (#o), \ + .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_PROTO_H */ diff --git a/xpp/xtalk/include/xtalk/proto_raw.h b/xpp/xtalk/include/xtalk/proto_raw.h new file mode 100644 index 0000000..c9a9bb6 --- /dev/null +++ b/xpp/xtalk/include/xtalk/proto_raw.h @@ -0,0 +1,76 @@ +#ifndef XTALK_PROTO_RAW_H +#define XTALK_PROTO_RAW_H +/* + * 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. + * + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * XTALKSYNC - Base synchronous protocol for our USB devices + * It is meant to provide a common base for layered + * protocols (dialects) + */ + +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#error "We do not know how your compiler packs structures" +#endif + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_raw; +struct xusb; + +XTALK_API struct xtalk_raw *xtalk_raw_new(struct xtalk_base *xtalk_base); +XTALK_API void xtalk_raw_delete(struct xtalk_raw *xraw); +XTALK_API int xtalk_raw_set_protocol(struct xtalk_raw *xtalk_base, + const struct xtalk_protocol *xproto); +XTALK_API int xtalk_raw_cmd_recv(struct xtalk_raw *xraw, + struct xtalk_command **reply_ref); +XTALK_API int xtalk_raw_cmd_send(struct xtalk_raw *xraw, const char *buf, int len, + uint16_t *tx_seq); + +/* + * These are low-level interfaces that receive/send arbitrary buffers + * Be carefull, as that allow to send illegal Xtalk packets + */ +XTALK_API int xtalk_raw_buffer_recv(struct xtalk_raw *xraw, char *buf, int len); +XTALK_API int xtalk_raw_buffer_send(struct xtalk_raw *xraw, const char *buf, int len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XTALK_PROTO_RAW_H */ diff --git a/xpp/xtalk/include/xtalk/proto_sync.h b/xpp/xtalk/include/xtalk/proto_sync.h new file mode 100644 index 0000000..b8895a7 --- /dev/null +++ b/xpp/xtalk/include/xtalk/proto_sync.h @@ -0,0 +1,75 @@ +#ifndef XTALK_PROTO_SYNC_H +#define XTALK_PROTO_SYNC_H +/* + * 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 + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * XTALKSYNC - Base synchronous protocol for our USB devices + * It is meant to provide a common base for layered + * protocols (dialects) + */ + +#include +#include +#include + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#error "We do not know how your compiler packs structures" +#endif + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_sync; +struct xusb; + +/* high-level */ +XTALK_API struct xtalk_sync *xtalk_sync_new(struct xtalk_base *xtalk_base); +XTALK_API void xtalk_sync_delete(struct xtalk_sync *xtalk_sync); +XTALK_API int xtalk_sync_set_protocol(struct xtalk_sync *xtalk_base, + const struct xtalk_protocol *xproto); +XTALK_API int xtalk_proto_query(struct xtalk_sync *dev); + +/* low-level */ +XTALK_API int process_command( + struct xtalk_sync *dev, + struct xtalk_command *cmd, + struct xtalk_command **reply_ref, + uint16_t *sequence_number); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XTALK_PROTO_SYNC_H */ diff --git a/xpp/xtalk/include/xtalk/xlist.h b/xpp/xtalk/include/xtalk/xlist.h new file mode 100644 index 0000000..3b1bae4 --- /dev/null +++ b/xpp/xtalk/include/xtalk/xlist.h @@ -0,0 +1,32 @@ +#ifndef XLIST_H +#define XLIST_H + +#include + +#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); + +XTALK_API struct xlist_node *xlist_new(void *data); +XTALK_API void xlist_destroy(struct xlist_node *list, xlist_destructor_t destructor); +XTALK_API void xlist_append_list(struct xlist_node *list1, struct xlist_node *list2); +XTALK_API void xlist_append_item(struct xlist_node *list, struct xlist_node *item); +XTALK_API void xlist_remove_item(struct xlist_node *item); +XTALK_API struct xlist_node *xlist_shift(struct xlist_node *list); +XTALK_API int xlist_empty(const struct xlist_node *list); +XTALK_API size_t xlist_length(const struct xlist_node *list); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XLIST_H */ diff --git a/xpp/xtalk/include/xtalk/xtalk_iface.h b/xpp/xtalk/include/xtalk/xtalk_iface.h new file mode 100644 index 0000000..4b08713 --- /dev/null +++ b/xpp/xtalk/include/xtalk/xtalk_iface.h @@ -0,0 +1,19 @@ +/* + * Wrappers for swig/python integration + */ + +struct Command { + struct xtalk_command *command; +}; + +struct Xtalksync { + struct xtalk_base *xtalk_base; + struct xtalk_sync *xtalk_sync; + struct XusbIface *py_xusb_iface; +}; + +struct Xtalkraw { + struct xtalk_base *xtalk_base; + struct xtalk_raw *xtalk_raw; + struct XusbIface *py_xusb_iface; +}; diff --git a/xpp/xtalk/include/xtalk/xusb.h b/xpp/xtalk/include/xtalk/xusb.h new file mode 100644 index 0000000..2544985 --- /dev/null +++ b/xpp/xtalk/include/xtalk/xusb.h @@ -0,0 +1,115 @@ +#ifndef XUSB_H +#define XUSB_H +/* + * Written by Oron Peled + * 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 +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * Xorcom usb handling + */ + +#define PACKET_SIZE 512 + +/* + * Specify the wanted device + */ +struct xusb_spec { + char *name; /* For debug/output purpose */ + /* What we will actually use */ + uint16_t vendor_id; + uint16_t product_id; +}; + +#define SPEC_HEAD(vendor_id_, product_id_, name_) \ + { \ + .name = (name_), \ + .vendor_id = (vendor_id_), \ + .product_id = (product_id_), \ + } + +XTALK_API void xusb_init_spec(struct xusb_spec *xusb_spec, + char *name, uint16_t vendor_id, uint16_t product_id); + +struct xusb_device; +struct xusb_iface; + +/* + * Prototypes + */ +typedef int (*xusb_filter_t)(const struct xusb_device *xusb_device, void *data); +XTALK_API struct xlist_node *xusb_find_byproduct(const struct xusb_spec *specs, + int numspecs, xusb_filter_t filterfunc, void *data); +XTALK_API struct xusb_device *xusb_find_bypath(const char *path); +XTALK_API struct xusb_iface *xusb_open_one(const struct xusb_spec *specs, int numspecs, + int interface_num, + xusb_filter_t filterfunc, void *data); + +/* + * A convenience filter + */ +XTALK_API int xusb_filter_bypath(const struct xusb_device *xusb_device, void *data); + +/* Device management */ +XTALK_API const struct xusb_spec *xusb_spec(const struct xusb_device *xusb_device); +XTALK_API void xusb_destroy(struct xusb_device *xusb_device); +XTALK_API size_t xusb_packet_size(const struct xusb_device *xusb_device); +XTALK_API void xusb_showinfo(const struct xusb_device *xusb_device); +XTALK_API const char *xusb_serial(const struct xusb_device *xusb_device); +XTALK_API const char *xusb_manufacturer(const struct xusb_device *xusb_device); +XTALK_API const char *xusb_product(const struct xusb_device *xusb_device); +XTALK_API uint16_t xusb_bus_num(const struct xusb_device *xusb_device); +XTALK_API uint16_t xusb_device_num(const struct xusb_device *xusb_device); +XTALK_API uint16_t xusb_vendor_id(const struct xusb_device *xusb_device); +XTALK_API uint16_t xusb_product_id(const struct xusb_device *xusb_device); +XTALK_API const char *xusb_devpath(const struct xusb_device *xusb_device); +XTALK_API const struct xusb_spec *xusb_device_spec(const struct xusb_device *xusb_device); +XTALK_API struct xusb_iface *xusb_find_iface(const char *devpath, + int iface_num, + int ep_out, + int ep_in, + struct xusb_spec *dummy_spec); +XTALK_API int xusb_claim(struct xusb_device *xusb_device, unsigned int interface_num, + struct xusb_iface **iface); +XTALK_API void xusb_release(struct xusb_iface *iface); +XTALK_API int xusb_is_claimed(struct xusb_iface *iface); +XTALK_API struct xusb_iface *xusb_interface_of(const struct xusb_device *dev, int num); +XTALK_API struct xusb_device *xusb_deviceof(struct xusb_iface *iface); +XTALK_API const char *xusb_interface_name(const struct xusb_iface *iface); +XTALK_API int xusb_interface_num(const struct xusb_iface *iface); +XTALK_API int xusb_send(struct xusb_iface *iface, const char *buf, int len, int timeout); +XTALK_API int xusb_recv(struct xusb_iface *iface, char *buf, size_t len, int timeout); +XTALK_API int xusb_flushread(struct xusb_iface *iface); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XUSB_H */ diff --git a/xpp/xtalk/include/xtalk/xusb_iface.h b/xpp/xtalk/include/xtalk/xusb_iface.h new file mode 100644 index 0000000..407f04a --- /dev/null +++ b/xpp/xtalk/include/xtalk/xusb_iface.h @@ -0,0 +1,43 @@ +/* + * Wrappers for swig/python integration + */ + +#ifdef SWIG +%feature("docstring", "Represents the specification of wanted USB device") Spec; +#endif +struct Spec { +#ifdef SWIG + %immutable spec; + %immutable ref_count; +#endif + struct xusb_spec *spec; + int ref_count; +}; + +#ifdef SWIG +%feature("docstring", "Represents a single USB device") XusbDev; +#endif +struct XusbDev { +#ifdef SWIG + %immutable spec; + %immutable xusb_device; + %immutable ref_count; +#endif + struct Spec *spec_wrapper; + struct xusb_device *xusb_device; + int ref_count; +}; + +#ifdef SWIG +%feature("docstring", "Represents a single USB interface") XusbIface; +#endif +struct XusbIface { +#ifdef SWIG + %immutable dev_wrapper; + %immutable iface; +#endif + struct XusbDev *dev_wrapper; /* for ref-counting */ + struct xusb_iface *iface; +}; + + diff --git a/xpp/xtalk/xlist.c b/xpp/xtalk/xlist.c index d8cd3df..bc2f1c6 100644 --- a/xpp/xtalk/xlist.c +++ b/xpp/xtalk/xlist.c @@ -1,7 +1,8 @@ #include #include #include -#include +#include +#include struct xlist_node *xlist_new(void *data) { @@ -36,6 +37,17 @@ void xlist_destroy(struct xlist_node *list, xlist_destructor_t destructor) free(list); } +void xlist_append_list(struct xlist_node *list1, struct xlist_node *list2) +{ + struct xlist_node *curr; + + assert(list1); + assert(list2); + + while ((curr = xlist_shift(list2)) != NULL) + xlist_append_item(list1, curr); +} + void xlist_append_item(struct xlist_node *list, struct xlist_node *item) { assert(list); diff --git a/xpp/xtalk/xlist_test.c b/xpp/xtalk/xlist_test.c new file mode 100644 index 0000000..d5e7180 --- /dev/null +++ b/xpp/xtalk/xlist_test.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +void dump_list(const struct xlist_node *list) +{ + struct xlist_node *curr; + const char *p; + int len; + + len = xlist_length(list); + p = list->data; + printf("dumping list: %s[%d]\n", p, len); + for (curr = list->next; curr != list; curr = curr->next) { + p = curr->data; + printf("> %s\n", p); + } +} + +void string_destructor(void *data) +{ + const char *p = data; + + printf("destroy: '%s'\n", p); +} + +int main() +{ + struct xlist_node *list1; + struct xlist_node *list2; + struct xlist_node *list3; + struct xlist_node *item1; + struct xlist_node *item2; + struct xlist_node *item3; + + list1 = xlist_new("list1"); + list2 = xlist_new("list2"); + list3 = xlist_new("list3"); + item1 = xlist_new("item1"); + item2 = xlist_new("item2"); + item3 = xlist_new("item3"); + assert(xlist_empty(list1)); + assert(xlist_empty(list2)); + assert(xlist_empty(list3)); + assert(xlist_empty(item1)); + assert(xlist_empty(item2)); + assert(xlist_empty(item3)); + dump_list(list1); + dump_list(list2); + xlist_append_item(list1, item1); + assert(!xlist_empty(list1)); + xlist_append_item(list1, item2); + xlist_append_item(list1, item3); + dump_list(list1); + xlist_remove_item(item2); + assert(!xlist_empty(list1)); + xlist_append_item(list2, item2); + assert(!xlist_empty(list2)); + dump_list(list1); + dump_list(list2); + xlist_shift(list1); + dump_list(list1); + xlist_append_list(list1, list2); + dump_list(list1); + xlist_append_item(list3, item1); + xlist_append_list(list1, list3); + dump_list(list1); + xlist_destroy(list1, string_destructor); + xlist_destroy(list2, string_destructor); + xlist_destroy(list3, string_destructor); + return 0; +} diff --git a/xpp/xtalk/xtalk-xusb.c b/xpp/xtalk/xtalk-xusb.c new file mode 100644 index 0000000..1ac9bf4 --- /dev/null +++ b/xpp/xtalk/xtalk-xusb.c @@ -0,0 +1,70 @@ +/* + * Written by Oron Peled + * Copyright (C) 2012, 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. + * + */ + +/* + * Convenience wrappers for xtalk_base over xusb + */ +#include +#include +#include +#include +#include "xtalk_base.h" + +static inline int close_func(void *priv) +{ + struct xusb_iface *iface = (struct xusb_iface *)priv; + xusb_release(iface); + return 0; +} + +static inline int send_func(void *priv, const char *data, size_t len, int timeout) +{ + return xusb_send((struct xusb_iface *)priv, data, len, timeout); +} + +static inline int recv_func(void *priv, char *data, size_t maxlen, int timeout) +{ + return xusb_recv((struct xusb_iface *)priv, data, maxlen, timeout); +} + + +static struct xtalk_ops xtalk_ops = { + .send_func = send_func, + .recv_func = recv_func, + .close_func = close_func, +}; + +struct xtalk_base *xtalk_base_new_on_xusb(struct xusb_iface *xusb_iface) +{ + struct xtalk_base *xtalk_base; + int packet_size; + + assert(xusb_iface); + packet_size = xusb_packet_size(xusb_deviceof(xusb_iface)); + + xtalk_base = xtalk_base_new(&xtalk_ops, packet_size, xusb_iface); + if (!xtalk_base) { + ERR("Failed creating the xtalk device abstraction\n"); + return NULL; + } + return xtalk_base; +} diff --git a/xpp/xtalk/xtalk_base.c b/xpp/xtalk/xtalk_base.c new file mode 100644 index 0000000..4dbb5ec --- /dev/null +++ b/xpp/xtalk/xtalk_base.c @@ -0,0 +1,353 @@ +/* + * 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 +#include "xtalk_base.h" + +#define DBG_MASK 0x02 + +void free_command(struct xtalk_command *cmd) +{ + if (!cmd) + return; + memset(cmd, 0, cmd->header.len); + free(cmd); +} + +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; +} + +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; +} + +const char *xtalk_protocol_name(const struct xtalk_base *xtalk_base) +{ + const struct xtalk_protocol *xproto; + + xproto = &xtalk_base->xproto; + return (xproto) ? xproto->name : ""; +} + +int xtalk_set_protocol(struct xtalk_base *xtalk_base, + const struct xtalk_protocol *xproto_base, + const struct xtalk_protocol *xproto) +{ + const char *protoname = (xproto) ? xproto->name : "GLOBAL"; + int i; + + DBG("%s\n", protoname); + memset(&xtalk_base->xproto, 0, sizeof(xtalk_base->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_base->xproto.commands[i] = *desc; + DBG("private: op=0x%X (%s)\n", i, desc->name); + } else { + if (!IS_PRIVATE_OP(i)) { + const char *name; + + xtalk_base->xproto.commands[i] = + xproto_base->commands[i]; + name = xtalk_base->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_base->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_base->xproto.ack_statuses[i] = + xproto_base->ack_statuses[i]; + stat_msg = xtalk_base->xproto.ack_statuses[i]; + if (stat_msg) + DBG("global: status=0x%X (%s)\n", + i, stat_msg); + } + } + } + xtalk_base->xproto.name = protoname; + xtalk_base->xproto.proto_version = (xproto) ? xproto->proto_version : 0; + return 0; +} + +int xtalk_cmd_callback(struct xtalk_base *xtalk_base, int op, + xtalk_cmd_callback_t callback, + xtalk_cmd_callback_t *old_callback) +{ + const struct xtalk_protocol *xproto; + const struct xtalk_command_desc *desc; + + xproto = &xtalk_base->xproto; + desc = get_command_desc(xproto, op); + if (!desc) + DBG("Unknown op=0x%X.\n", op); + if (old_callback) + *old_callback = xtalk_base->callbacks[op]; + if (callback) { + xtalk_base->callbacks[op] = callback; + DBG("OP=0x%X [%s] -- set callback to %p\n", + op, + (desc) ? desc->name : "", + callback); + } + return 0; +} + +struct xtalk_command *new_command( + const struct xtalk_base *xtalk_base, + 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_base->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; + cmd = malloc(len); + if (!cmd) { + 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]); +} + +int xtalk_set_timeout(struct xtalk_base *dev, int new_timeout) +{ + int old_timeout = dev->default_timeout; + dev->default_timeout = new_timeout; + return old_timeout; +} + +int send_buffer(struct xtalk_base *xtalk_base, const char *buf, int len) +{ + int ret; + void *priv = xtalk_base->transport_priv; + int timeout = xtalk_base->default_timeout; + + ret = xtalk_base->ops.send_func(priv, buf, len, timeout); + if (ret < 0) + DBG("%s: failed ret=%d\n", __func__, ret); + return ret; +} + +int recv_buffer(struct xtalk_base *xtalk_base, char *buf, int len) +{ + void *priv = xtalk_base->transport_priv; + int timeout = xtalk_base->default_timeout; + int ret; + + ret = xtalk_base->ops.recv_func(priv, buf, len, timeout); + if (ret < 0) { + DBG("Receive from usb failed (ret=%d)\n", ret); + goto out; + } else if (ret == 0) { + DBG("No reply\n"); + goto out; /* No reply */ + } + DBG("Received %d bytes\n", ret); +out: + return ret; +} + +int send_command(struct xtalk_base *xtalk_base, + struct xtalk_command *cmd, uint16_t *tx_seq) +{ + int ret; + int len; + + len = cmd->header.len; + cmd->header.seq = xtalk_base->tx_sequenceno; + ret = send_buffer(xtalk_base, (const char *)cmd, len); + if (ret < 0) + DBG("%s: failed ret=%d\n", __func__, ret); + else if (tx_seq) + *tx_seq = xtalk_base->tx_sequenceno++; + return ret; +} + +int recv_command(struct xtalk_base *xtalk_base, + struct xtalk_command **reply_ref) +{ + struct xtalk_command *reply; + size_t psize = xtalk_base->packet_size; + int ret; + + reply = malloc(psize); + if (!reply) { + ERR("Out of memory\n"); + ret = -ENOMEM; + goto err; + } + reply->header.len = 0; + ret = recv_buffer(xtalk_base, (char *)reply, psize); + if (ret < 0) { + DBG("%s: calling recv_buffer() failed (ret=%d)\n", __func__, ret); + 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, __func__, (char *)reply, ret); */ + *reply_ref = reply; + return ret; +err: + if (reply) { + memset(reply, 0, psize); + free_command(reply); + *reply_ref = NULL; + } + return ret; +} + +/* + * Wrappers + */ + +struct xtalk_base *xtalk_base_new(const struct xtalk_ops *ops, + size_t packet_size, void *priv) +{ + struct xtalk_base *xtalk_base; + + DBG("\n"); + assert(ops != NULL); + xtalk_base = calloc(1, sizeof(*xtalk_base)); + if (!xtalk_base) { + ERR("Allocating XTALK device memory failed\n"); + return NULL; + } + memcpy((void *)&xtalk_base->ops, (const void *)ops, + sizeof(xtalk_base->ops)); + xtalk_base->packet_size = packet_size; + xtalk_base->transport_priv = priv; + xtalk_base->tx_sequenceno = 1; + xtalk_base->default_timeout = 2000; /* millies */ + return xtalk_base; +} + +void xtalk_base_delete(struct xtalk_base *xtalk_base) +{ + void *priv; + + if (!xtalk_base) + return; + DBG("\n"); + priv = xtalk_base->transport_priv; + assert(priv); + xtalk_base->tx_sequenceno = 0; + assert(&xtalk_base->ops != NULL); + assert(&xtalk_base->ops.close_func != NULL); + xtalk_base->ops.close_func(priv); + memset(xtalk_base, 0, sizeof(*xtalk_base)); + free(xtalk_base); +} diff --git a/xpp/xtalk/xtalk_base.h b/xpp/xtalk/xtalk_base.h new file mode 100644 index 0000000..6923675 --- /dev/null +++ b/xpp/xtalk/xtalk_base.h @@ -0,0 +1,73 @@ +#ifndef XTALK_BASE_H +#define XTALK_BASE_H +/* + * 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 + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_base { + void *transport_priv; /* e.g: struct xusb */ + struct xtalk_ops ops; + struct xtalk_protocol xproto; + xtalk_cmd_callback_t callbacks[MAX_OPS]; + uint8_t xtalk_proto_version; + uint8_t status; + size_t packet_size; + uint16_t tx_sequenceno; + int default_timeout; /* in millies */ +}; + +int xtalk_set_protocol(struct xtalk_base *xtalk_base, + const struct xtalk_protocol *xproto_base, + const struct xtalk_protocol *xproto); +const struct xtalk_command_desc *get_command_desc( + const struct xtalk_protocol *xproto, uint8_t op); +int send_command(struct xtalk_base *xtalk_base, + struct xtalk_command *cmd, uint16_t *tx_seq); +int recv_command(struct xtalk_base *xtalk_base, + struct xtalk_command **reply_ref); + +/* + * These are low-level interfaces that receive/send arbitrary buffers + * Be carefull, as that allow to send illegal Xtalk packets + */ +int send_buffer(struct xtalk_base *xtalk_base, const char *buf, int len); +int recv_buffer(struct xtalk_base *xtalk_base, char *buf, int len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XTALK_BASE_H */ diff --git a/xpp/xtalk/xtalk_raw.c b/xpp/xtalk/xtalk_raw.c new file mode 100644 index 0000000..49e47d5 --- /dev/null +++ b/xpp/xtalk/xtalk_raw.c @@ -0,0 +1,221 @@ +/* + * 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 +#include +#include "xtalk_base.h" + +#define DBG_MASK 0x02 + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_raw { + struct xtalk_base *xtalk_base; +}; + +CMD_DEF(XTALK, ACK, + uint8_t stat; + ); + +union XTALK_PDATA(XTALK) { + MEMBER(XTALK, ACK); +} PACKED members; + +const struct xtalk_protocol xtalk_raw_proto = { + .name = "XTALK-RAW", + .proto_version = 0, + .commands = { + CMD_RECV(XTALK, ACK), + }, + .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"), + } +}; + +struct xtalk_raw *xtalk_raw_new(struct xtalk_base *xtalk_base) +{ + struct xtalk_raw *xtalk_raw; + int ret; + + assert(xtalk_base); + xtalk_raw = calloc(1, sizeof(*xtalk_raw)); + if (!xtalk_raw) { + ERR("Allocating XTALK device memory failed\n"); + return NULL; + } + xtalk_raw->xtalk_base = xtalk_base; + ret = xtalk_set_protocol(xtalk_raw->xtalk_base, &xtalk_raw_proto, NULL); + if (ret < 0) { + ERR("GLOBAL Protocol registration failed: %d\n", ret); + goto err; + } + DBG("%s: xtalk_raw=%p\n", __func__, xtalk_raw); + return xtalk_raw; +err: + xtalk_raw_delete(xtalk_raw); + return NULL; +} + +void xtalk_raw_delete(struct xtalk_raw *xtalk_raw) +{ + if (xtalk_raw) { + memset(xtalk_raw, 0, sizeof(*xtalk_raw)); + free(xtalk_raw); + } +} + +int xtalk_raw_set_protocol(struct xtalk_raw *xtalk_raw, + const struct xtalk_protocol *xproto) +{ + return xtalk_set_protocol(xtalk_raw->xtalk_base, &xtalk_raw_proto, xproto); +} + +int xtalk_raw_cmd_send(struct xtalk_raw *xtalk_raw, const char *buf, int len, + uint16_t *tx_seq) +{ + struct xtalk_command *cmd; + char *p; + int ret; + + p = malloc(len); + if (!p) { + ERR("allocation failed (%d bytes)\n", len); + return -ENOMEM; + } + cmd = (struct xtalk_command *)p; + memcpy(p, buf, len); + cmd->header.len = len; + + ret = send_command(xtalk_raw->xtalk_base, cmd, tx_seq); + if (ret < 0) { + DBG("%s: send_command(%d bytes) failed ret=%d\n", + __func__, len, ret); + goto out; + } + DBG("%s(%d bytes, tx_seq=%d)\n", __func__, len, *tx_seq); +out: + free(p); + return ret; +} + +__attribute__((warn_unused_result)) +int xtalk_raw_cmd_recv(struct xtalk_raw *xtalk_raw, + struct xtalk_command **reply_ref) +{ + struct xtalk_base *xtalk_base; + const struct xtalk_protocol *xproto; + struct xtalk_command *reply = NULL; + const struct xtalk_command_desc *reply_desc; + const char *protoname; + int ret; + xtalk_cmd_callback_t callback; + + xtalk_base = xtalk_raw->xtalk_base; + xproto = &xtalk_base->xproto; + protoname = (xproto) ? xproto->name : "GLOBAL"; + /* So the caller knows if a reply was received */ + if (reply_ref) + *reply_ref = NULL; + ret = recv_command(xtalk_base, &reply); + if (ret <= 0) { + DBG("%s: failed (xproto = %s, ret = %d)\n", + __func__, protoname, ret); + goto err; + } + DBG("REPLY OP: 0x%X\n", reply->header.op); + if (debug_mask & DBG_MASK) + xtalk_dump_command(reply); + /* reply_desc may be NULL (raw reply to application) */ + reply_desc = get_command_desc(xproto, reply->header.op); + if (reply->header.op == XTALK_ACK) { + int status = CMD_FIELD(reply, XTALK, ACK, stat); + + if (status != STAT_OK) { + ERR("Got NACK(seq=%d): %d %s\n", + reply->header.seq, + status, + ack_status_msg(xproto, status)); + } + /* Good expected ACK ... */ + } + DBG("got reply seq=%d op=0x%X (%d bytes)\n", + reply->header.seq, + reply->header.op, + ret); + /* Find if there is associated callback */ + ret = xtalk_cmd_callback(xtalk_base, reply->header.op, NULL, &callback); + if (ret < 0) { + ERR("Failed getting callback for op=0x%X\n", reply->header.op); + goto err; + } + if (callback) { + /* Override return value with callback return value */ + ret = callback(xtalk_base, reply_desc, reply); + DBG("%s: callback for 0x%X returned %d\n", __func__, + reply->header.op, ret); + } + if (reply_ref) + *reply_ref = reply; + return 0; +err: + if (reply) + free_command(reply); + return ret; +} + +int xtalk_raw_buffer_send(struct xtalk_raw *xraw, const char *buf, int len) +{ + dump_packet(LOG_DEBUG, DBG_MASK, __func__, buf, len); + return send_buffer(xraw->xtalk_base, buf, len); +} + +int xtalk_raw_buffer_recv(struct xtalk_raw *xraw, char *buf, int len) +{ + int ret; + + ret = recv_buffer(xraw->xtalk_base, buf, len); + if (ret >= 0) + dump_packet(LOG_DEBUG, DBG_MASK, __func__, buf, ret); + return ret; +} diff --git a/xpp/xtalk/xtalk_raw_test.c b/xpp/xtalk/xtalk_raw_test.c new file mode 100644 index 0000000..e693cf9 --- /dev/null +++ b/xpp/xtalk/xtalk_raw_test.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *progname; + +#define DBG_MASK 0x10 + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D [hexnum ....]\n", + progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, + "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + fprintf(stderr, "\tDevice:\n"); + fprintf(stderr, "\t\t/proc/bus/usb//\n"); + fprintf(stderr, "\t\t/dev/bus/usb//\n"); + exit(1); +} + +static void xusb_destructor(void *data) +{ + struct xusb_device *xusb_device = data; + xusb_destroy(xusb_device); +} + +static const struct test_struct { + struct xusb_spec spec; + int interface_num; +} known_devices[] = { + { SPEC_HEAD(0xe4e4, 0x1162, "astribank2-FPGA"), 0 }, + {{}, 0 }, +}; + +int proto_ack_cb( + const struct xtalk_base *xtalk_base, + const struct xtalk_command_desc *cmd_desc, + struct xtalk_command *cmd) +{ + const char *name = (cmd_desc->name) ? cmd_desc->name : "RAW"; + + printf("%s: op=0x%X (%s): len=%d\n", __func__, + cmd_desc->op, name, cmd->header.len); + return 0; +} + +/* + * Not const, because we override proto_version for testing + */ +static struct xtalk_protocol xtalk_raw_test_base = { + .name = "XTALK_TEST", + .proto_version = 0, /* Modified in test_device() */ + .commands = { + }, + .ack_statuses = { + } +}; + +static int test_device(struct xusb_iface *xusb_iface, int timeout) +{ + struct xtalk_base *xtalk_base = NULL; + struct xtalk_raw *xtalk_raw = NULL; + struct xtalk_command *reply; + uint16_t tx_seq; + int ret; + + xtalk_base = xtalk_base_new_on_xusb(xusb_iface); + if (!xtalk_base) { + ERR("Failed creating the xtalk device abstraction\n"); + ret = -ENOMEM; + goto err; + } + xtalk_raw = xtalk_raw_new(xtalk_base); + if (!xtalk_raw) { + ERR("Failed creating the xtalk sync device abstraction\n"); + ret = -ENOMEM; + goto err; + } + ret = xtalk_set_timeout(xtalk_base, timeout); + INFO("Original timeout=%d, now set to %d\n", ret, timeout); + /* override constness for testing */ + ret = xtalk_raw_set_protocol(xtalk_raw, &xtalk_raw_test_base); + if (ret < 0) { + ERR("%s Protocol registration failed: %d\n", + xtalk_raw_test_base.name, ret); + ret = -EPROTO; + goto err; + } + ret = xtalk_cmd_callback(xtalk_base, XTALK_OP(XTALK, ACK), proto_ack_cb, NULL); + if (ret < 0) { + ERR("%s Callback registration failed: %d\n", + xtalk_raw_test_base.name, ret); + ret = -EPROTO; + goto err; + } + INFO("Device and Protocol are ready\n"); + ret = xtalk_raw_cmd_send(xtalk_raw, "abcdef", 6, &tx_seq); + if (ret < 0) { + ERR("Failed sending raw command: %d\n", ret); + ret = -EPROTO; + goto err; + } + do { + ret = xtalk_raw_cmd_recv(xtalk_raw, &reply); + if (ret < 0) { + if (ret == -ETIMEDOUT) { + printf("timeout\n"); + continue; + } + ERR("Read error (ret=%d)\n", ret); + goto err; + } + assert(reply); + printf("Got %d (len=%d)\n", reply->header.op, reply->header.len); + } while (1); +err: + if (xtalk_raw) + xtalk_raw_delete(xtalk_raw); + if (xtalk_base) + xtalk_base_delete(xtalk_base); + return ret; +} + +static int run_spec(int i, xusb_filter_t filter, char *devpath, int timeout) +{ + const struct xusb_spec *s = &known_devices[i].spec; + int interface_num = known_devices[i].interface_num; + struct xlist_node *xlist; + struct xlist_node *curr; + struct xusb_device *xusb_device; + int success = 1; + + if (!s->name) + return 0; + xlist = xusb_find_byproduct(s, 1, filter, devpath); + if (!xlist_length(xlist)) + return 1; + INFO("total %zd devices of type %s\n", xlist_length(xlist), s->name); + for (curr = xlist_shift(xlist); curr; curr = xlist_shift(xlist)) { + struct xusb_iface *xusb_iface; + int ret; + + xusb_device = curr->data; + xusb_showinfo(xusb_device); + INFO("Testing interface %d\n", interface_num); + ret = xusb_claim(xusb_device, interface_num, &xusb_iface); + if (ret == 0) { + ret = test_device(xusb_iface, timeout); + if (ret < 0) + success = 0; + } + xusb_destroy(xusb_device); + } + xlist_destroy(xlist, xusb_destructor); + return success; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + const char options[] = "vd:D:EFpt:"; + xusb_filter_t filter = NULL; + int timeout = 500; /* millies */ + int i; + + progname = argv[0]; + while (1) { + int c; + + c = getopt(argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + filter = xusb_filter_bypath; + break; + case 'v': + verbose++; + break; + case 't': + timeout = strtoul(optarg, NULL, 0); + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + i = 0; + while (run_spec(i, filter, devpath, timeout)) + i++; + return 0; +} diff --git a/xpp/xtalk/xtalk_send.8 b/xpp/xtalk/xtalk_send.8 new file mode 100644 index 0000000..bef62e4 --- /dev/null +++ b/xpp/xtalk/xtalk_send.8 @@ -0,0 +1,49 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" (C) Copyright 2013 Oron Peled , +.\" +.TH XTALK_SEND 8 "March 14, 2013" +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +xtalk_send \- send arbitrary packets to XTALK devices +.SH SYNOPSIS +.B xtalk_send +.RI [ options ] " hexnum" ... +.br +.SH DESCRIPTION +This manual page documents briefly the \fBxtalk_send\fP command. +.PP +\fBxtalk_send\fP is a program that send arbitrary packets to Xorcom devices +that use the XTALK protocol. +.SH OPTIONS +A summary of options is included below: +.TP +.B \-I +Specify USB interface number (default 1). +.TP +.B \-t +Timeout from send until an answer is received (default 500). +.TP +.B \-Q +Query protocol version. This is only valid for synchronous XTALK protocols. +Usually, these protocols are bound to USB interface 1. +.TP +.B \-h +Show summary of options. +.TP +.B \-v +Increase output verbosity. +.TP +.B \-d +Debug mask. Use 0xFF for everything. +.SH AUTHOR +Oron Peled diff --git a/xpp/xtalk/xtalk_send.c b/xpp/xtalk/xtalk_send.c new file mode 100644 index 0000000..8f3424b --- /dev/null +++ b/xpp/xtalk/xtalk_send.c @@ -0,0 +1,220 @@ +/* + * Written by Oron Peled + * Copyright (C) 2008-2011, 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 +#include +#include +#include + +#define DBG_MASK 0x80 + +static char *progname; +static int timeout = 500; /* msec */ +static int iface_num = 1; + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D / [hexnum ....]\n", + progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, + "\t\t[-I] # Interface number (default %d)\n", + iface_num); + fprintf(stderr, + "\t\t[-t] # Timeout (default %d)\n", + timeout); + fprintf(stderr, "\t\t[-Q] # Query protocol version\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, + "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + fprintf(stderr, "\t\t[-h] # This help\n"); + exit(1); +} + +/***** XTALK Interface *****************************************************/ +static const struct xtalk_protocol xtalk_protocol = { + .name = "XTALK-DERIVED", + .proto_version = 0, + .commands = { + }, + .ack_statuses = { + } +}; + + +/***** XUSB Interface ******************************************************/ + +static int sendto_device(struct xusb_iface *iface, int nargs, char *args[]) +{ + char *reply = NULL; + char *buf = NULL; + int ret = 0; + int i; + + assert(nargs >= 0); + if (!nargs) + goto out; + buf = malloc(nargs); + if (!buf) { + ERR("Out of memory for %d command bytes\n", nargs); + ret = -ENOMEM; + goto out; + } + for (i = 0; i < nargs; i++) { + int val = strtoul(args[i], NULL, 16); + printf("%d> 0x%02X\n", i, val); + buf[i] = val; + } + ret = xusb_send(iface, buf, nargs, timeout); + if (ret < 0) { + ERR("xusb_send failed ret=%d\n", ret); + goto out; + } + reply = malloc(PACKET_SIZE); + if (!reply) { + ERR("Out of memory\n"); + ret = -ENOMEM; + goto out; + } + ret = xusb_recv(iface, reply, PACKET_SIZE, timeout); + if (ret < 0) { + ERR("Receive from usb failed.\n"); + goto out; + } + dump_packet(LOG_INFO, 0, "REPLY", reply, ret); + ret = 0; +out: + if (reply) + free(reply); + if (buf) + free(buf); + return ret; +} + +static int show_protocol(struct xtalk_sync *xtalk_sync, struct xusb_iface *iface) +{ + int ret; + + ret = xtalk_sync_set_protocol(xtalk_sync, &xtalk_protocol); + if (ret < 0) { + ERR("%s Protocol registration failed: %s\n", + xtalk_protocol.name, strerror(-ret)); + return ret; + } + ret = xtalk_proto_query(xtalk_sync); + if (ret < 0) { + ERR("Protocol query error: %s\n", strerror(-ret)); + return ret; + } + INFO("usb:%s: Protocol version 0x%X\n", xusb_devpath(xusb_deviceof(iface)), ret); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + struct xusb_device *xusb_device; + struct xtalk_base *xtalk_base; + struct xtalk_sync *xtalk_sync; + struct xusb_iface *iface; + const char options[] = "vd:D:t:I:i:o:Q"; + int query = 0; + int ret; + + progname = argv[0]; + while (1) { + int c; + + c = getopt(argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + break; + case 'I': + iface_num = strtoul(optarg, NULL, 0); + break; + case 't': + timeout = strtoul(optarg, NULL, 0); + break; + case 'Q': + query++; + break; + case 'v': + verbose++; + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + if (!devpath) { + ERR("Missing device path\n"); + usage(); + } + xusb_device = xusb_find_bypath(devpath); + if (!xusb_device) { + ERR("No XUSB device found\n"); + return 1; + } + ret = xusb_claim(xusb_device, iface_num, &iface); + if (ret < 0) { + ERR("Claiming interface #%d failed (ret = %d)\n", iface_num, ret); + return 1; + } + xusb_showinfo(xusb_deviceof(iface)); + xtalk_base = xtalk_base_new_on_xusb(iface); + if (!xtalk_base) { + ERR("Failed creating the xtalk device abstraction\n"); + return 1; + } + xtalk_sync = xtalk_sync_new(xtalk_base); + if (!xtalk_sync) { + ERR("Failed creating the xtalk device abstraction\n"); + return 1; + } + if (query) { + ret = show_protocol(xtalk_sync, iface); + if (ret < 0) + return 1; + } + ret = sendto_device(iface, argc - optind, argv + optind); + if (ret < 0) + ERR("Command failed: %d\n", ret); + xtalk_sync_delete(xtalk_sync); + xtalk_base_delete(xtalk_base); + xusb_destroy(xusb_deviceof(iface)); + return 0; +} diff --git a/xpp/xtalk/xtalk_sync.c b/xpp/xtalk/xtalk_sync.c new file mode 100644 index 0000000..c0be8fd --- /dev/null +++ b/xpp/xtalk/xtalk_sync.c @@ -0,0 +1,290 @@ +/* + * 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 +#include +#include "xtalk_base.h" + +#define DBG_MASK 0x02 + +/* + * Base XTALK device. A pointer to this struct + * should be included in the struct representing + * the dialect. + */ +struct xtalk_sync { + struct xtalk_base *xtalk_base; +}; + +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; + +const struct xtalk_protocol xtalk_sync_proto = { + .name = "XTALK-SYNC", + .proto_version = 0, + .commands = { + CMD_RECV(XTALK, ACK), + CMD_SEND(XTALK, PROTO_GET), + CMD_RECV(XTALK, PROTO_GET_REPLY), + }, + .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"), + } +}; + +struct xtalk_sync *xtalk_sync_new(struct xtalk_base *xtalk_base) +{ + struct xtalk_sync *xtalk_sync; + int ret; + + assert(xtalk_base); + xtalk_sync = calloc(1, sizeof(*xtalk_sync)); + if (!xtalk_sync) { + ERR("Allocating XTALK device memory failed\n"); + return NULL; + } + xtalk_sync->xtalk_base = xtalk_base; + ret = xtalk_set_protocol(xtalk_sync->xtalk_base, &xtalk_sync_proto, NULL); + if (ret < 0) { + ERR("GLOBAL Protocol registration failed: %d\n", ret); + goto err; + } + DBG("%s: xtalk_sync=%p\n", __func__, xtalk_sync); + return xtalk_sync; +err: + xtalk_sync_delete(xtalk_sync); + return NULL; +} + +void xtalk_sync_delete(struct xtalk_sync *xtalk_sync) +{ + if (xtalk_sync) { + memset(xtalk_sync, 0, sizeof(*xtalk_sync)); + free(xtalk_sync); + } +} + +int xtalk_sync_set_protocol(struct xtalk_sync *xtalk_sync, + const struct xtalk_protocol *xproto) +{ + return xtalk_set_protocol(xtalk_sync->xtalk_base, &xtalk_sync_proto, xproto); +} + +__attribute__((warn_unused_result)) +int process_command( + struct xtalk_sync *xtalk_sync, + struct xtalk_command *cmd, + struct xtalk_command **reply_ref, + uint16_t *tx_seq) +{ + struct xtalk_base *xtalk_base; + 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; + xtalk_cmd_callback_t callback; + + xtalk_base = xtalk_sync->xtalk_base; + xproto = &xtalk_base->xproto; + protoname = (xproto) ? xproto->name : "GLOBAL"; + /* So the caller knows if a reply was received */ + if (reply_ref) + *reply_ref = NULL; + reply_op = cmd->header.op | XTALK_REPLY_MASK; + cmd_desc = get_command_desc(xproto, cmd->header.op); + expected = get_command_desc(xproto, reply_op); + ret = send_command(xtalk_base, cmd, tx_seq); + if (ret < 0) { + ERR("send_command failed: %d\n", ret); + goto out; + } + if (!reply_ref) { + DBG("No reply requested\n"); + goto out; + } + ret = recv_command(xtalk_base, &reply); + if (ret <= 0) { + DBG("recv_command failed (ret = %d)\n", ret); + 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; + } + 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); + dump_packet(LOG_ERR, 0, __func__, (const char *)reply, ret); + ret = -EPROTO; + goto out; + } + DBG("REPLY OP: 0x%X [%s]\n", reply->header.op, 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)); + 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; + } + /* Find if there is associated callback */ + ret = xtalk_cmd_callback(xtalk_base, reply->header.op, NULL, &callback); + if (ret < 0) { + ERR("Failed getting callback for op=0x%X\n", reply->header.op); + goto out; + } + ret = reply->header.len; /* All good, return the length */ + DBG("got reply op 0x%X (%d bytes)\n", reply->header.op, ret); + if (callback) { + /* Override return value with callback return value */ + ret = callback(xtalk_base, reply_desc, reply); + DBG("%s: callback for 0x%X returned %d\n", __func__, + 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_sync *xtalk_sync) +{ + struct xtalk_base *xtalk_base; + struct xtalk_command *cmd; + struct xtalk_command *reply; + uint8_t proto_version; + int ret; + uint16_t tx_seq; + + DBG("\n"); + assert(xtalk_sync != NULL); + xtalk_base = xtalk_sync->xtalk_base; + proto_version = xtalk_base->xproto.proto_version; + cmd = new_command(xtalk_base, XTALK_OP(XTALK, PROTO_GET), 0); + if (!cmd) { + ERR("new_command failed\n"); + return -ENOMEM; + } + /* Protocol Version */ + CMD_FIELD(cmd, XTALK, PROTO_GET, proto_version) = proto_version; + CMD_FIELD(cmd, XTALK, PROTO_GET, reserved) = 0; /* RESERVED */ + ret = process_command(xtalk_sync, cmd, &reply, &tx_seq); + if (ret < 0) { + ERR("process_command failed: %d\n", ret); + goto out; + } + xtalk_base->xtalk_proto_version = + CMD_FIELD(reply, XTALK, PROTO_GET_REPLY, proto_version); + if (xtalk_base->xtalk_proto_version != proto_version) { + DBG("Got %s protocol version: 0x%02x (expected 0x%02x)\n", + xtalk_base->xproto.name, + xtalk_base->xtalk_proto_version, + proto_version); + ret = xtalk_base->xtalk_proto_version; + goto out; + } + DBG("Protocol version: %02x (tx_seq = %d)\n", + xtalk_base->xtalk_proto_version, tx_seq); + ret = xtalk_base->xtalk_proto_version; +out: + free_command(reply); + return ret; +} diff --git a/xpp/xtalk/xtalk_test.c b/xpp/xtalk/xtalk_test.c new file mode 100644 index 0000000..a77e061 --- /dev/null +++ b/xpp/xtalk/xtalk_test.c @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *progname; + +#define DBG_MASK 0x10 + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D [hexnum ....]\n", + progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, + "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + fprintf(stderr, "\tDevice:\n"); + fprintf(stderr, "\t\t/proc/bus/usb//\n"); + fprintf(stderr, "\t\t/dev/bus/usb//\n"); + exit(1); +} + +static void xusb_destructor(void *data) +{ + struct xusb_device *xusb_device = data; + xusb_destroy(xusb_device); +} + +#define KNOWN_DEV(p, i, v, d) \ + { SPEC_HEAD(0xe4e4, (p), (d)), (i), (v) } + +static const struct test_struct { + struct xusb_spec spec; + int interface_num; + uint8_t proto_version; +} known_devices[] = { + /* PROD I V NAME */ + KNOWN_DEV(0x1161, 1, 0x14, "astribank2-USB"), + KNOWN_DEV(0x1162, 1, 0x14, "astribank2-FPGA"), + KNOWN_DEV(0x1191, 1, 0x10, "xpanel"), + KNOWN_DEV(0x1183, 1, 0x11, "multi-ps"), + KNOWN_DEV(0x11a3, 0, 0x01, "auth-dongle"), + KNOWN_DEV(0xbb01, 1, 0x10, "iwc"), + KNOWN_DEV(0, 0, 0, NULL), +}; + +int proto_get_reply_cb( + const struct xtalk_base *xtalk_base, + const struct xtalk_command_desc *cmd_desc, + struct xtalk_command *cmd) +{ + INFO("CALLBACK: op=0x%X (%s): len=%d\n", + cmd_desc->op, cmd_desc->name, cmd->header.len); + return 0; +} + +/* + * Not const, because we override proto_version for testing + */ +static struct xtalk_protocol xtalk_test_base = { + .name = "XTALK_TEST", + .proto_version = 0, /* Modified in test_device() */ + .commands = { + }, + .ack_statuses = { + } +}; + +static int test_device(struct xusb_iface *xusb_iface, uint8_t wanted_version, int timeout) +{ + struct xtalk_base *xtalk_base; + struct xtalk_sync *xtalk_sync; + int proto_version; + int ret; + + xtalk_base = xtalk_base_new_on_xusb(xusb_iface); + if (!xtalk_base) { + ERR("Failed creating the xtalk device abstraction\n"); + return -ENOMEM; + } + xtalk_sync = xtalk_sync_new(xtalk_base); + if (!xtalk_sync) { + ERR("Failed creating the xtalk sync device abstraction\n"); + return -ENOMEM; + } + ret = xtalk_set_timeout(xtalk_base, timeout); + INFO("Original timeout=%d, now set to %d\n", ret, timeout); + /* override constness for testing */ + xtalk_test_base.proto_version = wanted_version; + ret = xtalk_sync_set_protocol(xtalk_sync, &xtalk_test_base); + if (ret < 0) { + ERR("%s Protocol registration failed: %d\n", + xtalk_test_base.name, ret); + return -EPROTO; + } + ret = xtalk_cmd_callback(xtalk_base, XTALK_OP(XTALK, PROTO_GET_REPLY), proto_get_reply_cb, NULL); + if (ret < 0) { + ERR("%s Callback registration failed: %d\n", + xtalk_test_base.name, ret); + return -EPROTO; + } + proto_version = xtalk_proto_query(xtalk_sync); + if (proto_version < 0) { + ERR("Protocol query error: %s\n", strerror(-proto_version)); + return proto_version; + } + if (proto_version != xtalk_test_base.proto_version) { + ERR("Bad protocol version: 0x%02x\n", proto_version); + return -EPROTO; + } + INFO("Device and Protocol are ready (proto_version=0x%X)\n", + proto_version); + xtalk_sync_delete(xtalk_sync); + xtalk_base_delete(xtalk_base); + return 0; +} + +static int run_spec(int i, xusb_filter_t filter, char *devpath, int timeout) +{ + const struct xusb_spec *s = &known_devices[i].spec; + int interface_num = known_devices[i].interface_num; + uint8_t proto_version = known_devices[i].proto_version; + struct xlist_node *xlist; + struct xlist_node *curr; + struct xusb_device *xusb_device; + int success = 1; + + if (!s->name) + return 0; + xlist = xusb_find_byproduct(s, 1, filter, devpath); + if (!xlist_length(xlist)) + return 1; + INFO("total %zd devices of type %s\n", xlist_length(xlist), s->name); + for (curr = xlist_shift(xlist); curr; curr = xlist_shift(xlist)) { + struct xusb_iface *xusb_iface; + int ret; + + xusb_device = curr->data; + xusb_showinfo(xusb_device); + INFO("Testing interface %d\n", interface_num); + ret = xusb_claim(xusb_device, interface_num, &xusb_iface); + if (ret == 0) { + ret = test_device(xusb_iface, proto_version, timeout); + if (ret < 0) + success = 0; + } + xusb_destroy(xusb_device); + } + xlist_destroy(xlist, xusb_destructor); + return success; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + const char options[] = "vd:D:EFpt:"; + xusb_filter_t filter = NULL; + int timeout = 500; /* millies */ + int i; + + progname = argv[0]; + while (1) { + int c; + + c = getopt(argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + filter = xusb_filter_bypath; + break; + case 'v': + verbose++; + break; + case 't': + timeout = strtoul(optarg, NULL, 0); + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + i = 0; + while (run_spec(i, filter, devpath, timeout)) + i++; + return 0; +} diff --git a/xpp/xtalk/xusb_common.c b/xpp/xtalk/xusb_common.c new file mode 100644 index 0000000..bccfd39 --- /dev/null +++ b/xpp/xtalk/xusb_common.c @@ -0,0 +1,381 @@ +#define _GNU_SOURCE /* for memrchr() */ +#include +#include +#include +#include +#include +#include +#include +#include "xusb_common.h" + +#define DBG_MASK 0x01 + +const char *xusb_tt_name(enum xusb_transfer_type tt) +{ + switch (tt) { + case XUSB_TT_BULK: return "BULK"; + case XUSB_TT_INTERRUPT: return "INTERRUPT"; + case XUSB_TT_ILLEGAL: + break; + } + return "ILLEGAL"; +} + +/* GCC versions before 4.6 did not support neither push and pop on + * the diagnostic pragma nor applying it inside a function. + */ +#ifndef HAVE_GCC_PRAGMA_DIAG_STACK +#pragma GCC diagnostic ignored "-Wformat-security" +#endif +int xusb_printf(const struct xusb_iface *iface, int level, int debug_mask, + const char *prefix, const char *fmt, ...) +{ + int n; + va_list ap; + char fmtbuf[BUFSIZ]; + char tmpbuf[BUFSIZ]; + + snprintf(fmtbuf, sizeof(fmtbuf), "%s%03d/%03d[%d] %s", + prefix, + xusb_bus_num(iface->xusb_device), + xusb_device_num(iface->xusb_device), + xusb_interface_num(iface), + fmt); + va_start(ap, fmt); + n = vsnprintf(tmpbuf, sizeof(tmpbuf), fmtbuf, ap); + va_end(ap); +#ifdef HAVE_GCC_PRAGMA_DIAG_STACK +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + log_function(level, debug_mask, tmpbuf); +#ifdef HAVE_GCC_PRAGMA_DIAG_STACK +#pragma GCC diagnostic pop +#endif + return n; +} +#ifndef HAVE_GCC_PRAGMA_DIAG_STACK +#pragma GCC diagnostic error "-Wformat-security" +#endif + +int xusb_printf_details(const struct xusb_iface *iface, int level, int debug_mask, + const char *file, int line, const char *severity, const char *func, + const char *fmt, ...) +{ + int n; + va_list ap; + char prefix[BUFSIZ]; + char tmpbuf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap); + va_end(ap); + snprintf(prefix, sizeof(prefix), "%s:%d: %s(%s): ", + file, line, severity, func); + va_start(ap, fmt); + n = xusb_printf(iface, level, DBG_MASK, prefix, tmpbuf); + va_end(ap); + return n; +} + +void xusb_init_spec(struct xusb_spec *spec, char *name, + uint16_t vendor_id, uint16_t product_id) +{ + DBG("Initialize [%02X:%02X] - %s\n", vendor_id, product_id, name); + memset(spec, 0, sizeof(*spec)); + spec->name = name; + spec->vendor_id = vendor_id; + spec->product_id = product_id; +} + +const struct xusb_spec *xusb_device_spec(const struct xusb_device *xusb_device) +{ + return xusb_device->spec; +} + +/* + * Match the string "tail" as the tail of string "path" + * Returns 1 in case they match, 0 otherwise + */ +int match_devpath(const char *path, const char *tail) +{ + int len_path = strlen(path); + int len_tail = strlen(tail); + int path_offset = len_path - len_tail; + + if (path_offset < 0) + return 0; + return strstr(path + path_offset, tail) != NULL; +} + +int match_device(const struct xusb_device *xusb_device, + const struct xusb_spec *spec) +{ + assert(xusb_device); + DBG("Checking: %04X:%04X: " + "\"%s\"\n", + spec->vendor_id, + spec->product_id, + spec->name); + if (xusb_device->idVendor != spec->vendor_id) { + DBG("Wrong vendor id 0x%X\n", xusb_device->idVendor); + return 0; + } + if (xusb_device->idProduct != spec->product_id) { + DBG("Wrong product id 0x%X\n", xusb_device->idProduct); + return 0; + } + return 1; +} + +struct xusb_device *xusb_deviceof(struct xusb_iface *iface) +{ + return iface->xusb_device; +} + +int xusb_is_claimed(struct xusb_iface *iface) +{ + return iface->is_claimed != 0; +} + +struct xusb_iface *xusb_interface_of(const struct xusb_device *dev, int num) +{ + return dev->interfaces[num]; +} + +#define XUSB_IFACE_DUMP(prefix, level, iface) \ + XUSB_PRINT((iface), level, "%s%d\tep_out=0x%2X ep_in=0x%02X [%s]\n", \ + (prefix), \ + (iface)->interface_num, \ + (iface)->ep_out, \ + (iface)->ep_in, \ + (iface)->iInterface) + +void xusb_list_dump(struct xlist_node *xusb_list) +{ + struct xlist_node *curr; + struct xusb_device *xusb_device; + + for (curr = xusb_list->next; curr != xusb_list; curr = curr->next) { + struct xusb_iface **piface; + + xusb_device = curr->data; + assert(xusb_device); + DBG("%s: usb:ID=%04X:%04X [%s / %s / %s]\n", + xusb_device->devpath_tail, + xusb_device->idVendor, + xusb_device->idProduct, + xusb_device->iManufacturer, + xusb_device->iProduct, + xusb_device->iSerialNumber + ); + for (piface = xusb_device->interfaces; *piface; piface++) + XUSB_IFACE_DUMP("\t", DEBUG, *piface); + } +} + +void xusb_destroy_interface(struct xusb_iface *iface) +{ + if (iface) { + xusb_release(iface); + XUSB_DBG(iface, "MEM: FREE interface\n"); + memset(iface, 0, sizeof(*iface)); + free(iface); + iface = NULL; + } +} + +static const char *path_tail(const char *path) +{ + const char *p; + + assert(path != NULL); + /* Find last '/' */ + p = memrchr(path, '/', strlen(path)); + if (!p) { + ERR("Missing a '/' in %s\n", path); + return NULL; + } + /* Search for a '/' before that */ + p = memrchr(path, '/', p - path); + if (!p) + p = path; /* No more '/' */ + else + p++; /* skip '/' */ + return p; +} + +int xusb_filter_bypath(const struct xusb_device *xusb_device, void *data) +{ + const char *p; + const char *path = data; + + DBG("%s\n", path); + assert(path != NULL); + p = path_tail(path); + if (strcmp(xusb_device->devpath_tail, p) != 0) { + DBG("%s: device path missmatch (!= '%s')\n", + xusb_device->devpath_tail, p); + return 0; + } + return 1; +} + +struct xusb_iface *xusb_open_one(const struct xusb_spec *specs, int numspecs, + int interface_num, + xusb_filter_t filterfunc, void *data) +{ + struct xlist_node *xusb_list; + struct xlist_node *curr; + int num; + struct xusb_device *xusb_device = NULL; + struct xusb_iface *iface = NULL; + int ret; + + 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_device = curr->data; + xlist_destroy(curr, NULL); + xlist_destroy(xusb_list, NULL); + ret = xusb_claim(xusb_device, interface_num, &iface); + if (ret < 0) { + ERR("%s: Failed claiming interface %d (ret = %d)\n", + xusb_device->devpath_tail, + interface_num, + ret); + xusb_destroy(xusb_device); + return NULL; + } + break; + default: + ERR("Too many devices (%d). Aborting.\n", num); + break; + } + return iface; +} + +int xusb_interface_num(const struct xusb_iface *iface) +{ + return iface->interface_num; +} + +uint16_t xusb_vendor_id(const struct xusb_device *xusb_device) +{ + return xusb_device->idVendor; +} + +uint16_t xusb_product_id(const struct xusb_device *xusb_device) +{ + return xusb_device->idProduct; +} + +size_t xusb_packet_size(const struct xusb_device *xusb_device) +{ + return xusb_device->packet_size; +} + +const char *xusb_serial(const struct xusb_device *xusb_device) +{ + return xusb_device->iSerialNumber; +} + +const char *xusb_devpath(const struct xusb_device *xusb_device) +{ + return xusb_device->devpath_tail; +} + +uint16_t xusb_bus_num(const struct xusb_device *xusb_device) +{ + return xusb_device->bus_num; +} + +uint16_t xusb_device_num(const struct xusb_device *xusb_device) +{ + return xusb_device->device_num; +} + +const char *xusb_interface_name(const struct xusb_iface *iface) +{ + return iface->iInterface; +} + +const char *xusb_manufacturer(const struct xusb_device *xusb_device) +{ + return xusb_device->iManufacturer; +} + +const char *xusb_product(const struct xusb_device *xusb_device) +{ + return xusb_device->iProduct; +} + +const struct xusb_spec *xusb_spec(const struct xusb_device *xusb_device) +{ + return xusb_device->spec; +} + +int xusb_flushread(struct xusb_iface *iface) +{ + char tmpbuf[BUFSIZ]; + int ret; + + XUSB_DBG(iface, "starting...\n"); + memset(tmpbuf, 0, BUFSIZ); + ret = xusb_recv(iface, tmpbuf, BUFSIZ, 1); + if (ret < 0 && ret != -ETIMEDOUT) { + XUSB_ERR(iface, "ret=%d\n", ret); + return ret; + } else if (ret > 0) { + XUSB_DBG(iface, "Got %d bytes:\n", ret); + dump_packet(LOG_DEBUG, DBG_MASK, __func__, tmpbuf, ret); + } + return 0; +} + +static int use_clear_halt = 1; + +static int xtalk_one_option(const char *option_string) +{ + if (strcmp(option_string, "use-clear-halt") == 0) { + use_clear_halt = 1; + return 0; + } + if (strcmp(option_string, "no-use-clear-halt") == 0) { + use_clear_halt = 0; + return 0; + } + ERR("Unknown XTALK_OPTIONS content: '%s'\n", option_string); + return -EINVAL; +} + +int xtalk_option_use_clear_halt(void) +{ + return use_clear_halt; +} + +int xtalk_parse_options(void) +{ + char *xtalk_options; + char *saveptr; + char *token; + int ret; + + xtalk_options = getenv("XTALK_OPTIONS"); + if (!xtalk_options) + return 0; + token = strtok_r(xtalk_options, " \t", &saveptr); + while (token) { + ret = xtalk_one_option(token); + if (ret < 0) + return ret; + token = strtok_r(NULL, " \t", &saveptr); + } + return 0; +} diff --git a/xpp/xtalk/xusb_common.h b/xpp/xtalk/xusb_common.h new file mode 100644 index 0000000..ed411ed --- /dev/null +++ b/xpp/xtalk/xusb_common.h @@ -0,0 +1,104 @@ +#ifndef XUSB_COMMON_H +#define XUSB_COMMON_H +/* + * Written by Oron Peled + * Copyright (C) 2012, 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 + +/* + * XTALK_OPTIONS: + * - "use-clear-halt" + * - "no-use-clear-halt" + */ +int xtalk_parse_options(void); +int xtalk_option_use_clear_halt(void); + +enum xusb_transfer_type { + XUSB_TT_ILLEGAL = 0, + XUSB_TT_BULK, + XUSB_TT_INTERRUPT, +}; + +struct xusb_iface { + struct xusb_device *xusb_device; + int interface_num; + int ep_out; + int ep_in; + enum xusb_transfer_type transfer_type; + int is_claimed; + char iInterface[BUFSIZ]; +}; +struct libusb_implementation; + +#define XUSB_MAX_INTERFACES 32 + +struct xusb_device { + struct libusb_implementation *impl; + const struct xusb_spec *spec; + int idVendor; + int idProduct; + 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 is_usb2; + size_t packet_size; + struct xusb_iface *interfaces[XUSB_MAX_INTERFACES]; +}; + +#define EP_OUT(iface) ((iface)->ep_out) +#define EP_IN(iface) ((iface)->ep_in) + +int match_devpath(const char *path, const char *tail); +int match_device(const struct xusb_device *xusb_device, + const struct xusb_spec *spec); +void xusb_list_dump(struct xlist_node *xusb_list); +void xusb_destroy_interface(struct xusb_iface *iface); +int xusb_close(struct xusb_device *xusb_device); + +enum xusb_transfer_type xusb_transfer_type(const struct xusb_iface *iface); +const char *xusb_tt_name(enum xusb_transfer_type tt); + + +int xusb_printf(const struct xusb_iface *iface, int level, int debug_mask, + const char *prefix, const char *fmt, ...); + +int xusb_printf_details(const struct xusb_iface *iface, int level, int debug_mask, + const char *file, int line, const char *severity, const char *func, + const char *fmt, ...); + +#define XUSB_PRINT(iface, level, fmt, arg...) \ + xusb_printf(iface, LOG_ ## level, 0, #level ": ", fmt, ## arg) + +#define XUSB_PRINT_DETAILS(iface, level, debug_mask, fmt, arg...) \ + xusb_printf_details(iface, LOG_ ## level, debug_mask, \ + __FILE__, __LINE__, #level, __func__, fmt, ## arg) + +#define XUSB_INFO(iface, fmt, arg...) XUSB_PRINT(iface, INFO, fmt, ## arg) +#define XUSB_ERR(iface, fmt, arg...) XUSB_PRINT_DETAILS(iface, ERR, 0, fmt, ## arg) +#define XUSB_DBG(iface, fmt, arg...) XUSB_PRINT_DETAILS(iface, DEBUG, DBG_MASK, fmt, ## arg) + +#endif /* XUSB_COMMON_H */ diff --git a/xpp/xtalk/xusb_libusb.c b/xpp/xtalk/xusb_libusb.c new file mode 100644 index 0000000..0edbb98 --- /dev/null +++ b/xpp/xtalk/xusb_libusb.c @@ -0,0 +1,843 @@ +/* + * Written by Oron Peled + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xusb_common.h" + +#define DBG_MASK 0x01 +#define TIMEOUT 500 +#define MAX_RETRIES 10 + +#define EP_IS_IN(ep) (((ep) & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_IN) +#define EP_IS_OUT(ep) (((ep) & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_OUT) + +struct libusb_implementation { + struct usb_device *dev; + usb_dev_handle *handle; + struct usb_config_descriptor *config_desc; +}; + +static void xusb_init(); + +/* + * USB handling + */ + +static int get_usb_string(struct xusb_device *xusb_device, uint8_t item, char *buf) +{ + char tmp[BUFSIZ]; + int ret; + + if (!xusb_device->impl->handle) { + ERR("%s: device closed\n", xusb_device->devpath_tail); + return -ENXIO; + } + if (!item) + return 0; + ret = usb_get_string_simple(xusb_device->impl->handle, item, tmp, BUFSIZ); + if (ret <= 0) + return ret; + return snprintf(buf, BUFSIZ, "%s", tmp); +} + +static const struct usb_interface_descriptor *get_iface_descriptor( + const struct xusb_device *xusb_device, int i) +{ + const struct usb_config_descriptor *config_desc; + const struct usb_interface *interface; + const struct usb_interface_descriptor *iface_desc; + int num_altsetting; + + assert(xusb_device); + assert(xusb_device->impl); + config_desc = xusb_device->impl->config_desc; + assert(config_desc); + assert(config_desc->bNumInterfaces < XUSB_MAX_INTERFACES); + if (i >= XUSB_MAX_INTERFACES) + return NULL; + interface = &config_desc->interface[i]; + assert(interface != NULL); + iface_desc = interface->altsetting; + num_altsetting = interface->num_altsetting; + assert(num_altsetting != 0); + assert(iface_desc != NULL); + return iface_desc; +} + +#define GET_USB_STRING(xusb_device, from, item) \ + get_usb_string((xusb_device), (from)->item, (xusb_device)->item) + +static int xusb_fill_strings(struct xusb_device *xusb_device, int interface_num) +{ + const struct usb_device_descriptor *dev_desc; + const struct usb_interface_descriptor *iface_desc; + struct xusb_iface *iface = xusb_device->interfaces[interface_num]; + int ret; + + dev_desc = &xusb_device->impl->dev->descriptor; + assert(dev_desc); + ret = GET_USB_STRING(xusb_device, dev_desc, iManufacturer); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iManufacturer string: %s\n", + usb_strerror()); + return 0; + } + ret = GET_USB_STRING(xusb_device, dev_desc, iProduct); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iProduct string: %s\n", + usb_strerror()); + return 0; + } + ret = GET_USB_STRING(xusb_device, dev_desc, iSerialNumber); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iSerialNumber string: %s\n", + usb_strerror()); + return 0; + } + iface_desc = get_iface_descriptor(xusb_device, interface_num); + if (!iface_desc) { + XUSB_ERR(iface, "Could not get interface descriptor of device\n"); + return 0; + } + ret = GET_USB_STRING(xusb_device, iface_desc, iInterface); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iInterface string: %s\n", + usb_strerror()); + return 0; + } + return 1; +} + +static int xusb_open(struct xusb_device *xusb_device) +{ + int ret = 0; + + assert(xusb_device); + DBG("%s\n", xusb_device->devpath_tail); + if (xusb_device->impl->handle) { + ERR("%s: already open\n", xusb_device->devpath_tail); + ret = -EBUSY; + goto out; + } + xusb_device->impl->handle = usb_open(xusb_device->impl->dev); + if (!xusb_device->impl->handle) { + ERR("%s: Failed to open usb device: %s\n", + xusb_device->devpath_tail, + usb_strerror()); + xusb_device->impl->handle = NULL; + goto out; + } + return 1; +out: + return ret; +} + +void xusb_release(struct xusb_iface *iface) +{ + if (iface && iface->is_claimed) { + usb_dev_handle *handle; + int ret; + + assert(iface->xusb_device); + handle = iface->xusb_device->impl->handle; + XUSB_DBG(iface, "Releasing interface\n"); + if (!handle) { + XUSB_ERR(iface, "device closed\n"); + iface->is_claimed = 0; + return; + } + ret = usb_release_interface(handle, iface->interface_num); + if (ret < 0) + XUSB_ERR(iface, "Releasing interface: %s\n", + usb_strerror()); + iface->is_claimed = 0; + } +} + +static int xusb_clear_halt(struct xusb_iface *xusb_iface) +{ + struct xusb_device *xusb_device; + int ret = 0; + int ep; + + xusb_device = xusb_iface->xusb_device; + /* + * WE DO NOT CALL HALT for problematic devices: + * - It cause problem with our usb-dongle (cypress CY7C63803, interrupt driven) + */ + if (xusb_device->idVendor == 0xe4e4 && xusb_device->idProduct == 0x11a3) { + XUSB_DBG(xusb_iface, "Skipping clear_halt()\n"); + goto out; + } + if (!xtalk_option_use_clear_halt()) { + XUSB_DBG(xusb_iface, "Don't use clear_halt()\n"); + goto out; + } + ep = EP_OUT(xusb_iface); + ret = usb_clear_halt(xusb_device->impl->handle, ep); + if (ret < 0) { + XUSB_ERR(xusb_iface, "Clearing output endpoint 0x%02X: %s\n", + ep, usb_strerror()); + goto out; + } + ep = EP_IN(xusb_iface); + ret = usb_clear_halt(xusb_device->impl->handle, ep); + if (ret < 0) { + XUSB_ERR(xusb_iface, "Clearing input endpoint 0x%02X: %s\n", + ep, usb_strerror()); + goto out; + } +out: + return ret; +} + +int xusb_claim(struct xusb_device *xusb_device, unsigned int interface_num, + struct xusb_iface **xusb_iface) +{ + struct xusb_iface *iface = NULL; + enum xusb_transfer_type iface_tt = XUSB_TT_ILLEGAL; + int ret = 0; + + *xusb_iface = NULL; + assert(xusb_device); + if (!xusb_device->impl->handle) { + XUSB_ERR(iface, "device closed\n"); + ret = -ENXIO; + goto failed; + } + if (interface_num >= XUSB_MAX_INTERFACES) { + ERR("%s: interface number %d is too big\n", + xusb_device->devpath_tail, interface_num); + ret = -EINVAL; + goto failed; + } + iface = xusb_device->interfaces[interface_num]; + if (!iface) { + ERR("%s: No interface number %d\n", + xusb_device->devpath_tail, interface_num); + ret = -EINVAL; + goto failed; + } + if (iface->is_claimed) { + XUSB_ERR(iface, "Already claimed\n"); + ret = -EBUSY; + goto failed; + } + ret = usb_claim_interface(xusb_device->impl->handle, iface->interface_num); + if (ret < 0) { + XUSB_ERR(iface, "usb_claim_interface: %s\n", usb_strerror()); + goto failed; + } + iface->is_claimed = 1; + iface_tt = xusb_transfer_type(iface); + if (iface_tt == XUSB_TT_ILLEGAL) { + ret = -ENOTSUP; + goto failed; + } + iface->transfer_type = iface_tt; + ret = xusb_clear_halt(iface); + if (ret < 0) + goto failed; + ret = xusb_flushread(iface); + if (ret < 0) { + XUSB_ERR(iface, "xusb_flushread failed: %s\n", usb_strerror()); + goto failed; + } + xusb_fill_strings(xusb_device, interface_num); + XUSB_DBG(iface, "ID=%04X:%04X Manufacturer=[%s] Product=[%s] " + "SerialNumber=[%s] Interface=[%s] TT=%s\n", + xusb_device->idVendor, + xusb_device->idProduct, + xusb_device->iManufacturer, + xusb_device->iProduct, + xusb_device->iSerialNumber, + iface->iInterface, + xusb_tt_name(iface->transfer_type)); + *xusb_iface = iface; + return 0; +failed: + xusb_release(iface); + return ret; +} + +void xusb_destroy(struct xusb_device *xusb_device) +{ + if (xusb_device) { + struct xusb_iface **piface; + struct libusb_implementation *impl; + + for (piface = xusb_device->interfaces; *piface; piface++) { + xusb_destroy_interface(*piface); + *piface = NULL; + } + impl = xusb_device->impl; + if (impl) { + if (impl->handle) { + xusb_close(xusb_device); + impl->handle = NULL; + } + } + DBG("%s: MEM: FREE device\n", xusb_device->devpath_tail); + memset(xusb_device, 0, sizeof(*xusb_device)); + free(xusb_device); + xusb_device = NULL; + } +} + +static int init_interfaces(struct xusb_device *xusb_device) +{ + const struct usb_config_descriptor *config_desc; + const struct usb_interface_descriptor *iface_desc; + struct xusb_iface *iface; + int max_packet_size = 0; + int packet_size; + int if_idx; + + assert(xusb_device); + assert(xusb_device->impl); + config_desc = xusb_device->impl->config_desc; + assert(config_desc); + assert(config_desc->bNumInterfaces < XUSB_MAX_INTERFACES); + for (if_idx = 0; if_idx < config_desc->bNumInterfaces; if_idx++) { + int ep_idx; + + iface_desc = get_iface_descriptor(xusb_device, if_idx); + if (iface_desc->bInterfaceNumber != if_idx) { + ERR("%s: interface %d is number %d\n", + xusb_device->devpath_tail, + if_idx, iface_desc->bInterfaceNumber); + return -EINVAL; + } + if (iface_desc->bNumEndpoints != 2) { + ERR("%s: interface %d has %d endpoints\n", + xusb_device->devpath_tail, + if_idx, iface_desc->bNumEndpoints); + return -EINVAL; + } + iface = calloc(sizeof(*iface), 1); + if (!iface) { + ERR("%s: interface %d -- out of memory\n", + xusb_device->devpath_tail, if_idx); + return -ENOMEM; + } + DBG("MEM: ALLOC interface: %p\n", iface); + xusb_device->interfaces[if_idx] = iface; + iface->xusb_device = xusb_device; + iface->interface_num = iface_desc->bInterfaceNumber; + + /* Search Endpoints */ + iface->ep_in = 0; + iface->ep_out = 0; + for (ep_idx = 0; ep_idx < iface_desc->bNumEndpoints; ep_idx++) { + int ep_num; + + ep_num = iface_desc->endpoint[ep_idx].bEndpointAddress; + packet_size = iface_desc->endpoint[ep_idx].wMaxPacketSize; + if (!max_packet_size || packet_size < max_packet_size) + max_packet_size = packet_size; + if (EP_IS_OUT(ep_num)) + iface->ep_out = ep_num; + if (EP_IS_IN(ep_num)) + iface->ep_in = ep_num; + } + /* Validation */ + if (!iface->ep_out) { + ERR("%s[%d]: Missing output endpoint\n", + xusb_device->devpath_tail, if_idx); + return -EINVAL; + } + if (!iface->ep_in) { + ERR("%s[%d]: Missing input endpoint\n", + xusb_device->devpath_tail, if_idx); + return -EINVAL; + } + + iface->is_claimed = 0; + XUSB_DBG(iface, "ep_out=0x%X ep_in=0x%X packet_size=%d\n", + iface->ep_out, iface->ep_in, max_packet_size); + } + if (xusb_device->packet_size < max_packet_size) + xusb_device->packet_size = max_packet_size; + xusb_device->is_usb2 = (xusb_device->packet_size == 512); + return 0; +} + +static struct xusb_device *xusb_new(struct usb_device *dev, + const struct xusb_spec *spec, + xusb_filter_t filterfunc, + void *data) +{ + struct usb_device_descriptor *dev_desc; + int ret; + struct xusb_device *xusb_device = NULL; + + xusb_device = calloc(sizeof(*xusb_device) + sizeof(struct libusb_implementation), 1); + if (!xusb_device) { + ERR("Out of memory"); + goto fail; + } + DBG("MEM: ALLOC device: %p\n", xusb_device); + /* Fill xusb_device */ + xusb_device->impl = (void *)xusb_device + sizeof(*xusb_device); + xusb_device->impl->dev = dev; + xusb_device->spec = spec; + /* + * Get information from the usb_device + */ + dev_desc = &dev->descriptor; + if (!dev_desc) { + ERR("usb device without a device descriptor\n"); + goto fail; + } + xusb_device->idVendor = dev_desc->idVendor; + xusb_device->idProduct = dev_desc->idProduct; + sscanf(dev->bus->dirname, "%d", &xusb_device->bus_num); + sscanf(dev->filename, "%d", &xusb_device->device_num); + snprintf(xusb_device->devpath_tail, PATH_MAX, "%03d/%03d", + xusb_device->bus_num, xusb_device->device_num); + if (!match_device(xusb_device, spec)) { + DBG("[%04X:%04X] did not match\n", + xusb_device->idVendor, xusb_device->idProduct); + goto fail; + } + xusb_device->impl->config_desc = dev->config; + if (!xusb_device->impl->config_desc) { + ERR("usb device without a configuration descriptor\n"); + goto fail; + } + ret = init_interfaces(xusb_device); + if (ret < 0) { + ERR("%s: init_interfaces() failed (ret = %d)\n", + xusb_device->devpath_tail, ret); + goto fail; + } + if (!xusb_open(xusb_device)) { + ERR("%s: Failed opening device: %04X:%04X\n", + xusb_device->devpath_tail, + xusb_device->idVendor, + xusb_device->idProduct); + goto fail; + } + DBG("%s: %04X:%04X\n", + xusb_device->devpath_tail, + xusb_device->idVendor, + xusb_device->idProduct); + return xusb_device; +fail: + xusb_destroy(xusb_device); + return NULL; +} + +struct xusb_iface *xusb_find_iface(const char *devpath, + int iface_num, + int ep_out, + int ep_in, + struct xusb_spec *dummy_spec) +{ + return NULL; +} + +struct xusb_device *xusb_find_bypath(const char *path) +{ + struct usb_bus *bus; + struct usb_device *dev; + char devpath_tail[PATH_MAX]; + struct xusb_spec *spec; + + DBG("path='%s'\n", path); + spec = calloc(sizeof(*spec), 1); + if (!spec) { + ERR("Failed allocating spec\n"); + goto failed; + } + xusb_init(); + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + struct usb_device_descriptor *dev_desc; + struct xusb_device *xusb_device = NULL; + + 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); + snprintf(devpath_tail, PATH_MAX, "%3s/%3s", + dev->bus->dirname, dev->filename); + if (!match_devpath(path, devpath_tail)) + continue; + DBG("Found: usb:%s/%s: ID=%04X:%04X\n", + dev->bus->dirname, + dev->filename, + dev_desc->idVendor, + dev_desc->idProduct); + xusb_init_spec(spec, "", + dev_desc->idVendor, dev_desc->idProduct); + xusb_device = xusb_new(dev, spec, NULL, NULL); + if (!xusb_device) { + ERR("Failed creating xusb for %s\n", + devpath_tail); + xusb_init_spec(spec, "", 0, 0); + continue; + } + return xusb_device; + } + } +failed: + if (spec) + free(spec); + return NULL; +} + +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); + xlist = xlist_new(NULL); + if (!xlist) { + ERR("Failed allocation new xlist"); + goto failed; + } + xusb_init(); + 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_device *xusb_device; + const struct xusb_spec *sp = &specs[i]; + + xusb_device = xusb_new(dev, sp, filterfunc, data); + if (!xusb_device) + continue; + if (filterfunc && !filterfunc(xusb_device, data)) { + DBG("%s: %04X:%04X filtered out\n", + xusb_device->devpath_tail, + dev_desc->idVendor, + dev_desc->idProduct); + xusb_destroy(xusb_device); + continue; + } + item = xlist_new(xusb_device); + xlist_append_item(xlist, item); + break; + } + } + } + xusb_list_dump(xlist); + return xlist; +failed: + if (xlist) + xlist_destroy(xlist, NULL); + return NULL; +} + +void xusb_showinfo(const struct xusb_device *xusb_device) +{ + struct usb_device_descriptor *dev_desc; + struct usb_device *dev; + const struct xusb_iface **piface; + + assert(xusb_device); + dev = xusb_device->impl->dev; + assert(dev); + dev_desc = &dev->descriptor; + assert(dev_desc); + if (verbose <= LOG_INFO) { + INFO("%s: [%04X:%04X] [%s / %s / %s]\n", + xusb_device->devpath_tail, + dev_desc->idVendor, + dev_desc->idProduct, + xusb_device->iManufacturer, + xusb_device->iProduct, + xusb_device->iSerialNumber); + } else { + printf("USB Bus/Device: [%s/%s] (%s)\n", + dev->bus->dirname, + dev->filename, + (xusb_device->impl->handle) ? "open" : "closed"); + printf("USB Spec name: [%s]\n", xusb_device->spec->name); + printf("USB iManufacturer: [%s]\n", xusb_device->iManufacturer); + printf("USB iProduct: [%s]\n", xusb_device->iProduct); + printf("USB iSerialNumber: [%s]\n", xusb_device->iSerialNumber); + piface = (const struct xusb_iface **)xusb_device->interfaces; + for (; *piface; piface++) { + printf("USB Interface[%d]: ep_out=0x%02X ep_in=0x%02X claimed=%d [%s]\n", + (*piface)->interface_num, + (*piface)->ep_out, + (*piface)->ep_in, + (*piface)->is_claimed, + (*piface)->iInterface); + } + } +} + +int xusb_close(struct xusb_device *xusb_device) +{ + if (xusb_device && xusb_device->impl && xusb_device->impl->handle) { + assert(xusb_device->spec); + assert(xusb_device->spec->name); + DBG("%s: Closing device \"%s\"\n", + xusb_device->devpath_tail, + xusb_device->spec->name); + usb_close(xusb_device->impl->handle); + xusb_device->impl->handle = NULL; + } + return 0; +} + +enum xusb_transfer_type xusb_transfer_type(const struct xusb_iface *iface) +{ + const struct xusb_device *xusb_device; + const struct usb_interface_descriptor *iface_desc; + const struct usb_endpoint_descriptor *ep; + enum xusb_transfer_type ret = XUSB_TT_ILLEGAL; + + assert(iface); + xusb_device = iface->xusb_device; + assert(xusb_device); + iface_desc = get_iface_descriptor(xusb_device, iface->interface_num); + assert(iface_desc); + ep = iface_desc->endpoint; + assert(ep); + switch (ep->bmAttributes) { + case USB_ENDPOINT_TYPE_CONTROL: + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + break; + case USB_ENDPOINT_TYPE_BULK: + ret = XUSB_TT_BULK; + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + ret = XUSB_TT_INTERRUPT; + break; + } + return ret; +} + +int xusb_send(struct xusb_iface *iface, const char *buf, int len, int timeout) +{ + struct xusb_device *xusb_device = iface->xusb_device; + int ep_out = EP_OUT(iface); + int retries = 0; + int ret; + + dump_packet(LOG_DEBUG, DBG_MASK, __func__, buf, len); + if (!xusb_device->impl->handle) { + XUSB_ERR(iface, "device closed\n"); + return -ENXIO; + } + if (!EP_IS_OUT(ep_out)) { + XUSB_ERR(iface, "%s called with an input endpoint 0x%x\n", + __func__, ep_out); + return -EINVAL; + } +retry_write: + switch (iface->transfer_type) { + case XUSB_TT_BULK: + ret = usb_bulk_write(xusb_device->impl->handle, ep_out, (char *)buf, len, timeout); + break; + case XUSB_TT_INTERRUPT: + ret = usb_interrupt_write(xusb_device->impl->handle, ep_out, (char *)buf, len, timeout); + break; + default: + ret = -EINVAL; + break; + } + if (ret < 0) { + /* + * If the device was gone, it may be the + * result of renumeration. Ignore it. + */ + if (ret != -ENODEV) { + XUSB_ERR(iface, "write to endpoint 0x%x failed: (%d) %s\n", + ep_out, ret, usb_strerror()); + dump_packet(LOG_ERR, DBG_MASK, "xusb_send[ERR]", + buf, len); + /*exit(2);*/ + } else { + XUSB_DBG(iface, "write to endpoint 0x%x got ENODEV\n", + ep_out); + xusb_close(xusb_device); + } + return ret; + } + if (!ret) { + + XUSB_ERR(iface, "write to endpoint 0x%x short write[%d]: (%d)\n", + ep_out, retries, ret); + if (retries++ > MAX_RETRIES) + return -EFAULT; + usleep(100); + goto retry_write; + } + if (ret != len) { + XUSB_ERR(iface, "write to endpoint 0x%x short write: (%d) %s\n", + ep_out, ret, usb_strerror()); + dump_packet(LOG_ERR, DBG_MASK, "xusb_send[ERR]", buf, len); + return -EFAULT; + } + return ret; +} + +int xusb_recv(struct xusb_iface *iface, char *buf, size_t len, int timeout) +{ + struct xusb_device *xusb_device = iface->xusb_device; + int ep_in = EP_IN(iface); + int retries = 0; + int ret; + + if (!xusb_device->impl->handle) { + XUSB_ERR(iface, "device closed\n"); + return -ENXIO; + } + if (!EP_IS_IN(ep_in)) { + XUSB_ERR(iface, "called with an output endpoint 0x%x\n", ep_in); + return -EINVAL; + } +retry_read: + switch (iface->transfer_type) { + case XUSB_TT_BULK: + ret = usb_bulk_read(xusb_device->impl->handle, ep_in, buf, len, timeout); + break; + case XUSB_TT_INTERRUPT: + ret = usb_interrupt_read(xusb_device->impl->handle, ep_in, buf, len, timeout); + break; + default: + ret = -EAFNOSUPPORT; + break; + } + if (ret < 0) { + XUSB_DBG(iface, "read from endpoint 0x%x failed: (%d) %s\n", + ep_in, ret, usb_strerror()); + memset(buf, 0, len); + return ret; + } + if (!ret) { + XUSB_ERR(iface, "read to endpoint 0x%x short read[%d]: (%d)\n", + ep_in, retries, ret); + if (retries++ > MAX_RETRIES) + return -EFAULT; + usleep(100); + goto retry_read; + } + dump_packet(LOG_DEBUG, DBG_MASK, __func__, buf, ret); + return ret; +} + +/* + * Serialize calls to usb_find_busses()/usb_find_devices() + */ + +static const key_t SEM_KEY = 0x1a2b3c4d; +static int semid = -1; /* Failure */ + +static void xusb_lock_usb() +{ + struct sembuf sembuf; + + while (semid < 0) { + /* Maybe it was already created? */ + semid = semget(SEM_KEY, 1, 0); + if (semid < 0) { + /* No, let's create ourselves */ + semid = semget(SEM_KEY, 1, IPC_CREAT | IPC_EXCL | 0644); + if (semid < 0) { + /* Someone else won the race to create it */ + if (errno != ENOENT) + ERR("%s: semget() failed: %s\n", + __func__, strerror(errno)); + /* Retry */ + continue; + } + /* Initialize */ + if (semctl(semid, 0, SETVAL, 1) < 0) + ERR("%s: SETVAL() failed: %s\n", + __func__, strerror(errno)); + } + } + DBG("%d: LOCKING\n", getpid()); + sembuf.sem_num = 0; + sembuf.sem_op = -1; + sembuf.sem_flg = SEM_UNDO; + if (semop(semid, &sembuf, 1) < 0) + ERR("%s: semop() failed: %s\n", __func__, strerror(errno)); + DBG("%d: LOCKED\n", getpid()); +} + +static void xusb_unlock_usb() +{ + struct sembuf sembuf; + + DBG("%d: UNLOCKING\n", getpid()); + sembuf.sem_num = 0; + sembuf.sem_op = 1; + sembuf.sem_flg = SEM_UNDO; + if (semop(semid, &sembuf, 1) < 0) + ERR("%s: semop() failed: %s\n", __func__, strerror(errno)); + DBG("%d: UNLOCKED\n", getpid()); +} + +static int initizalized; + +void __attribute__((constructor)) xusb_init(void) +{ + xtalk_parse_options(); + if (!getenv("XUSB_NOLOCK")) + xusb_lock_usb(); + if (!initizalized) { + usb_init(); + initizalized = 1; + } + usb_find_busses(); + usb_find_devices(); + if (!getenv("XUSB_NOLOCK")) + xusb_unlock_usb(); +} diff --git a/xpp/xtalk/xusb_libusbx.c b/xpp/xtalk/xusb_libusbx.c new file mode 100644 index 0000000..69670df --- /dev/null +++ b/xpp/xtalk/xusb_libusbx.c @@ -0,0 +1,826 @@ +/* + * Written by Oron Peled + * Copyright (C) 2012, 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 +#include +#include +#include +#include +#include "xusb_common.h" + +#define DBG_MASK 0x01 +#define TIMEOUT 500 + +#define EP_IS_IN(ep) (((ep) & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) +#define EP_IS_OUT(ep) (((ep) & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + +struct libusb_implementation { + struct libusb_device *dev; + struct libusb_device_handle *handle; + struct libusb_config_descriptor *config_desc; +}; + +void __attribute__((constructor)) xusb_init(void) +{ + int ret; + + xtalk_parse_options(); + ret = libusb_init(NULL); + if (ret < 0) { + ERR("libusb_init() failed: ret=%d\n", ret); + abort(); + } +} + +void __attribute__((destructor)) xusb_fini(void) +{ + libusb_exit(NULL); +} + +/* + * Translate libusbx error codes to regular errno + */ +static int errno_map(int libusb_err) +{ + int ret = -libusb_err; +#define ERRNO_MAP(libusb, errno) [-(libusb)] = errno + static const int error_codes[] = { + ERRNO_MAP(LIBUSB_SUCCESS, 0), + ERRNO_MAP(LIBUSB_ERROR_IO, EIO), + ERRNO_MAP(LIBUSB_ERROR_INVALID_PARAM, EINVAL), + ERRNO_MAP(LIBUSB_ERROR_ACCESS, EACCES), + ERRNO_MAP(LIBUSB_ERROR_NO_DEVICE, ENODEV), + ERRNO_MAP(LIBUSB_ERROR_NOT_FOUND, ENOENT), + ERRNO_MAP(LIBUSB_ERROR_BUSY, EBUSY), + ERRNO_MAP(LIBUSB_ERROR_TIMEOUT, ETIMEDOUT), + ERRNO_MAP(LIBUSB_ERROR_OVERFLOW, EOVERFLOW), + ERRNO_MAP(LIBUSB_ERROR_PIPE, EPIPE), + ERRNO_MAP(LIBUSB_ERROR_INTERRUPTED, EINTR), + ERRNO_MAP(LIBUSB_ERROR_NO_MEM, ENOMSG), + ERRNO_MAP(LIBUSB_ERROR_NOT_SUPPORTED, ENOTSUP), + ERRNO_MAP(LIBUSB_ERROR_OTHER, EPROTO), + }; +#undef ERRNO_MAP + if (ret < 0 || ret > sizeof(error_codes)/sizeof(error_codes[0])) { + ERR("%s: Bad return code %d\n", __func__, -ret); + return -EPROTO; + } + return -(error_codes[ret]); +} + + +/* + * USB handling + */ + +static int get_usb_string(struct xusb_device *xusb_device, uint8_t item, char *buf) +{ + unsigned char tmp[BUFSIZ]; + int ret; + + if (!xusb_device->impl->handle) { + ERR("%s: device closed\n", xusb_device->devpath_tail); + return -ENXIO; + } + if (!item) + return 0; + ret = libusb_get_string_descriptor_ascii(xusb_device->impl->handle, item, + tmp, BUFSIZ); + if (ret <= 0) + return errno_map(ret); + return snprintf(buf, BUFSIZ, "%s", tmp); +} + +static const struct libusb_interface_descriptor *get_iface_descriptor( + const struct xusb_device *xusb_device, int i) +{ + const struct libusb_config_descriptor *config_desc; + const struct libusb_interface *interface; + const struct libusb_interface_descriptor *iface_desc; + + assert(xusb_device); + assert(xusb_device->impl); + config_desc = xusb_device->impl->config_desc; + assert(config_desc); + assert(config_desc->bNumInterfaces < XUSB_MAX_INTERFACES); + if (i >= XUSB_MAX_INTERFACES) + return NULL; + interface = &config_desc->interface[i]; + iface_desc = interface->altsetting; + return iface_desc; +} + +#define GET_USB_STRING(xusb_device, from, item) \ + get_usb_string((xusb_device), (from)->item, (xusb_device)->item) + +static int xusb_fill_strings(struct xusb_device *xusb_device, int interface_num) +{ + struct libusb_device_descriptor dev_desc; + const struct libusb_interface_descriptor *iface_desc; + struct xusb_iface *iface = xusb_device->interfaces[interface_num]; + int ret; + + assert(xusb_device); + assert(xusb_device->impl); + ret = libusb_get_device_descriptor(xusb_device->impl->dev, &dev_desc); + if (ret) { + XUSB_ERR(iface, "libusb_get_device_descriptor() failed: %s\n", + libusb_error_name(ret)); + return errno_map(ret); + } + ret = GET_USB_STRING(xusb_device, &dev_desc, iManufacturer); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iManufacturer string: %s\n", + libusb_error_name(ret)); + return 0; + } + ret = GET_USB_STRING(xusb_device, &dev_desc, iProduct); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iProduct string: %s\n", + libusb_error_name(ret)); + return 0; + } + ret = GET_USB_STRING(xusb_device, &dev_desc, iSerialNumber); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iSerialNumber string: %s\n", + libusb_error_name(ret)); + return 0; + } + iface_desc = get_iface_descriptor(xusb_device, interface_num); + if (!iface_desc) { + XUSB_ERR(iface, "Could not get interface descriptor of device\n"); + return 0; + } + ret = get_usb_string(xusb_device, iface_desc->iInterface, iface->iInterface); + if (ret < 0) { + XUSB_ERR(iface, "Failed reading iInterface string: %s\n", + libusb_error_name(ret)); + return 0; + } + return 1; +} + +static int xusb_open(struct xusb_device *xusb_device) +{ + int ret = 0; + + DBG("%s\n", xusb_device->devpath_tail); + if (xusb_device->impl->handle) { + ERR("%s: already open\n", xusb_device->devpath_tail); + ret = -EBUSY; + goto out; + } + ret = libusb_open(xusb_device->impl->dev, &(xusb_device->impl->handle)); + if (ret < 0) { + ERR("%s: Failed to open usb device: %s\n", + xusb_device->devpath_tail, + libusb_error_name(ret)); + ret = errno_map(ret); + xusb_device->impl->handle = NULL; + goto out; + } +out: + return ret; +} + +void xusb_release(struct xusb_iface *iface) +{ + if (iface && iface->is_claimed) { + struct libusb_device_handle *handle; + + assert(iface->xusb_device); + handle = iface->xusb_device->impl->handle; + XUSB_DBG(iface, "Releasing interface\n"); + if (!handle) { + XUSB_ERR(iface, "device closed\n"); + iface->is_claimed = 0; + return; + } + int ret = libusb_release_interface(handle, iface->interface_num); + if (ret < 0) + XUSB_ERR(iface, "Releasing interface: %s\n", + libusb_error_name(ret)); + iface->is_claimed = 0; + } +} + +static int xusb_clear_halt(struct xusb_iface *xusb_iface) +{ + struct xusb_device *xusb_device; + int ret = 0; + int ep; + + xusb_device = xusb_iface->xusb_device; + /* + * WE DO NOT CALL HALT for problematic devices: + * - It cause problem with our usb-dongle (cypress CY7C63803, interrupt driven) + */ + if (xusb_device->idVendor == 0xe4e4 && xusb_device->idProduct == 0x11a3) { + XUSB_DBG(xusb_iface, "Skipping clear_halt()\n"); + goto out; + } + if (!xtalk_option_use_clear_halt()) { + XUSB_DBG(xusb_iface, "Don't use clear_halt()\n"); + goto out; + } + ep = EP_OUT(xusb_iface); + ret = libusb_clear_halt(xusb_device->impl->handle, ep); + if (ret < 0) { + XUSB_ERR(xusb_iface, "Clearing output endpoint 0x%02X: %s\n", + ep, libusb_error_name(ret)); + ret = errno_map(ret); + goto out; + } + ep = EP_IN(xusb_iface); + ret = libusb_clear_halt(xusb_device->impl->handle, ep); + if (ret < 0) { + XUSB_ERR(xusb_iface, "Clearing input endpoint 0x%02X: %s\n", + ep, libusb_error_name(ret)); + ret = errno_map(ret); + goto out; + } +out: + return ret; +} + +int xusb_claim(struct xusb_device *xusb_device, unsigned int interface_num, + struct xusb_iface **xusb_iface) +{ + struct xusb_iface *iface = NULL; + enum xusb_transfer_type iface_tt = XUSB_TT_ILLEGAL; + int ret = 0; + + *xusb_iface = NULL; + assert(xusb_device); + if (!xusb_device->impl->handle) { + ERR("%s: device closed\n", xusb_device->devpath_tail); + return -ENXIO; + } + if (interface_num >= XUSB_MAX_INTERFACES) { + ERR("%s: interface number %d is too big\n", + xusb_device->devpath_tail, interface_num); + ret = -EINVAL; + goto failed; + } + iface = xusb_device->interfaces[interface_num]; + if (!iface) { + ERR("%s: No interface number %d\n", + xusb_device->devpath_tail, interface_num); + ret = -EINVAL; + goto failed; + } + if (iface->is_claimed) { + XUSB_ERR(iface, "Already claimed\n"); + ret = -EBUSY; + goto failed; + } + ret = libusb_claim_interface(xusb_device->impl->handle, iface->interface_num); + if (ret < 0) { + XUSB_ERR(iface, "libusb_claim_interface: %s\n", + libusb_error_name(ret)); + ret = errno_map(ret); + goto failed; + } + iface->is_claimed = 1; + iface_tt = xusb_transfer_type(iface); + if (iface_tt == XUSB_TT_ILLEGAL) { + ret = -ENOTSUP; + goto failed; + } + iface->transfer_type = iface_tt; + xusb_fill_strings(xusb_device, interface_num); + XUSB_DBG(iface, "ID=%04X:%04X Manufacturer=[%s] Product=[%s] " + "SerialNumber=[%s] Interface=[%s] TT=%s\n", + xusb_device->idVendor, + xusb_device->idProduct, + xusb_device->iManufacturer, + xusb_device->iProduct, + xusb_device->iSerialNumber, + iface->iInterface, + xusb_tt_name(iface->transfer_type)); + ret = xusb_clear_halt(iface); + if (ret < 0) + goto failed; + ret = xusb_flushread(iface); + if (ret < 0) { + XUSB_ERR(iface, "xusb_flushread failed: %d\n", ret); + goto failed; + } + *xusb_iface = iface; + return 0; +failed: + if (iface) + xusb_release(iface); + return ret; +} + +void xusb_destroy(struct xusb_device *xusb_device) +{ + if (xusb_device) { + struct xusb_iface **piface; + struct libusb_implementation *impl; + + for (piface = xusb_device->interfaces; *piface; piface++) { + xusb_destroy_interface(*piface); + *piface = NULL; + } + impl = xusb_device->impl; + if (impl) { + if (impl->handle) { + xusb_close(xusb_device); + impl->handle = NULL; + } + if (impl->config_desc) + libusb_free_config_descriptor(impl->config_desc); + } + DBG("%s: MEM: FREE device\n", xusb_device->devpath_tail); + memset(xusb_device, 0, sizeof(*xusb_device)); + free(xusb_device); + xusb_device = NULL; + } +} + +static int init_interfaces(struct xusb_device *xusb_device) +{ + const struct libusb_config_descriptor *config_desc; + const struct libusb_interface_descriptor *iface_desc; + struct xusb_iface *iface; + int max_packet_size = 0; + int packet_size; + int if_idx; + + assert(xusb_device); + assert(xusb_device->impl); + config_desc = xusb_device->impl->config_desc; + assert(config_desc->bNumInterfaces < XUSB_MAX_INTERFACES); + for (if_idx = 0; if_idx < config_desc->bNumInterfaces; if_idx++) { + int ep_idx; + + iface_desc = get_iface_descriptor(xusb_device, if_idx); + if (iface_desc->bInterfaceNumber != if_idx) { + ERR("%s: interface %d is number %d\n", + xusb_device->devpath_tail, + if_idx, iface_desc->bInterfaceNumber); + return -EINVAL; + } + if (iface_desc->bNumEndpoints != 2) { + ERR("%s: interface %d has %d endpoints\n", + xusb_device->devpath_tail, + if_idx, iface_desc->bNumEndpoints); + return -EINVAL; + } + iface = calloc(sizeof(*iface), 1); + if (!iface) { + ERR("%s: interface %d -- out of memory\n", + xusb_device->devpath_tail, if_idx); + return -ENOMEM; + } + DBG("MEM: ALLOC interface: %p\n", iface); + xusb_device->interfaces[if_idx] = iface; + iface->xusb_device = xusb_device; + iface->interface_num = iface_desc->bInterfaceNumber; + + /* Search Endpoints */ + iface->ep_in = 0; + iface->ep_out = 0; + for (ep_idx = 0; ep_idx < iface_desc->bNumEndpoints; ep_idx++) { + int ep_num; + + ep_num = iface_desc->endpoint[ep_idx].bEndpointAddress; + packet_size = libusb_get_max_packet_size(xusb_device->impl->dev, ep_num); + if (!max_packet_size || packet_size < max_packet_size) + max_packet_size = packet_size; + if (EP_IS_OUT(ep_num)) + iface->ep_out = ep_num; + if (EP_IS_IN(ep_num)) + iface->ep_in = ep_num; + } + /* Validation */ + if (!iface->ep_out) { + ERR("%s[%d]: Missing output endpoint\n", + xusb_device->devpath_tail, if_idx); + return -EINVAL; + } + if (!iface->ep_in) { + ERR("%s[%d]: Missing input endpoint\n", + xusb_device->devpath_tail, if_idx); + return -EINVAL; + } + + iface->is_claimed = 0; + XUSB_DBG(iface, "ep_out=0x%X ep_in=0x%X packet_size=%d\n", + iface->ep_out, iface->ep_in, max_packet_size); + } + if (xusb_device->packet_size < max_packet_size) + xusb_device->packet_size = max_packet_size; + xusb_device->is_usb2 = (xusb_device->packet_size == 512); + return 0; +} + +static struct xusb_device *xusb_new(struct libusb_device *dev, + const struct xusb_spec *spec) +{ + struct libusb_device_descriptor dev_desc; + int ret; + struct xusb_device *xusb_device = NULL; + + xusb_device = calloc(sizeof(*xusb_device) + sizeof(struct libusb_implementation), 1); + if (!xusb_device) { + ERR("Out of memory"); + goto fail; + } + DBG("MEM: ALLOC device: %p\n", xusb_device); + /* Fill xusb_device */ + xusb_device->impl = (void *)xusb_device + sizeof(*xusb_device); + xusb_device->impl->dev = dev; + xusb_device->spec = spec; + xusb_device->bus_num = libusb_get_bus_number(dev); + xusb_device->device_num = libusb_get_device_address(dev); + snprintf(xusb_device->devpath_tail, PATH_MAX, "%03d/%03d", + xusb_device->bus_num, xusb_device->device_num); + /* + * Opening the device + */ + ret = xusb_open(xusb_device); + if (ret < 0) { + ERR("%s: Failed to open usb device: %s\n", + xusb_device->devpath_tail, + strerror(-ret)); + goto fail; + } + /* + * Get information from the usb_device + */ + ret = libusb_get_device_descriptor(dev, &dev_desc); + if (ret) { + ERR("%s: libusb_get_device_descriptor() failed: %s\n", + xusb_device->devpath_tail, + libusb_error_name(ret)); + goto fail; + } + xusb_device->idVendor = dev_desc.idVendor; + xusb_device->idProduct = dev_desc.idProduct; + if (!match_device(xusb_device, spec)) { + DBG("[%04X:%04X] did not match\n", + xusb_device->idVendor, xusb_device->idProduct); + goto fail; + } + DBG("%s: process [%X:%X]\n", + xusb_device->devpath_tail, + xusb_device->idVendor, + xusb_device->idProduct); + ret = libusb_get_config_descriptor(dev, 0, &xusb_device->impl->config_desc); + if (ret) { + ERR("%s: libusb_get_config_descriptor() failed: %s\n", + xusb_device->devpath_tail, + libusb_error_name(ret)); + goto fail; + } + ret = init_interfaces(xusb_device); + if (ret < 0) { + ERR("%s: init_interfaces() failed (ret = %d)\n", + xusb_device->devpath_tail, ret); + goto fail; + } + DBG("%s: Created %04X:%04X\n", + xusb_device->devpath_tail, + xusb_device->idVendor, + xusb_device->idProduct); + return xusb_device; +fail: + xusb_destroy(xusb_device); + return NULL; +} + +struct xusb_iface *xusb_find_iface(const char *devpath, + int iface_num, + int ep_out, + int ep_in, + struct xusb_spec *dummy_spec) +{ + ERR("FIXME: Unimplemented yet\n"); + return NULL; +} + +struct xusb_device *xusb_find_bypath(const char *path) +{ + struct xusb_spec *spec; + libusb_device **list; + ssize_t cnt; + int i; + + DBG("path='%s'\n", path); + spec = calloc(sizeof(*spec), 1); + if (!spec) { + ERR("Failed allocating spec\n"); + goto failed; + } + cnt = libusb_get_device_list(NULL, &list); + if (cnt < 0) { + ERR("libusb_get_device_list() failed"); + goto failed; + } + for (i = 0; i < cnt; i++) { + struct libusb_device_descriptor dev_desc; + libusb_device *dev = list[i]; + struct xusb_device *xusb_device; + char devpath_tail[PATH_MAX]; + int bus_num; + int device_num; + int ret; + + bus_num = libusb_get_bus_number(dev); + device_num = libusb_get_device_address(dev); + snprintf(devpath_tail, PATH_MAX, "%03d/%03d", + bus_num, device_num); + if (!match_devpath(path, devpath_tail)) + continue; + ret = libusb_get_device_descriptor(dev, &dev_desc); + if (ret < 0) { + ERR("usb device without a device descriptor\n"); + continue; + } + INFO("Found: %04x:%04x %s\n", + dev_desc.idVendor, dev_desc.idProduct, devpath_tail); + xusb_init_spec(spec, "", + dev_desc.idVendor, dev_desc.idProduct); + xusb_device = xusb_new(dev, spec); + if (!xusb_device) { + ERR("Failed creating xusb for %s\n", + devpath_tail); + xusb_init_spec(spec, "", 0, 0); + continue; + } + return xusb_device; + } +failed: + if (spec) + free(spec); + return NULL; +} + +struct xlist_node *xusb_find_byproduct(const struct xusb_spec *specs, + int numspecs, xusb_filter_t filterfunc, void *data) +{ + struct xlist_node *xlist; + libusb_device **list; + ssize_t cnt; + int i; + + DBG("specs(%d)\n", numspecs); + xlist = xlist_new(NULL); + if (!xlist) { + ERR("Failed allocation new xlist"); + goto cleanup; + } + cnt = libusb_get_device_list(NULL, &list); + if (cnt < 0) { + ERR("libusb_get_device_list() failed"); + goto cleanup; + } + for (i = 0; i < cnt; i++) { + struct libusb_device_descriptor dev_desc; + libusb_device *dev = list[i]; + struct xlist_node *item; + int ret; + int j; + + ret = libusb_get_device_descriptor(dev, &dev_desc); + if (ret < 0) { + ERR("usb device without a device descriptor\n"); + continue; + } + for (j = 0; j < numspecs; j++) { + struct xusb_device *xusb_device; + const struct xusb_spec *sp = &specs[j]; + + xusb_device = xusb_new(dev, sp); + if (!xusb_device) + continue; + if (filterfunc && !filterfunc(xusb_device, data)) { + DBG("%s: %04X:%04X filtered out\n", + xusb_device->devpath_tail, + dev_desc.idVendor, + dev_desc.idProduct); + xusb_destroy(xusb_device); + continue; + } + item = xlist_new(xusb_device); + xlist_append_item(xlist, item); + break; + } + } + xusb_list_dump(xlist); + return xlist; +cleanup: + if (xlist) + xlist_destroy(xlist, NULL); + return NULL; +} + +void xusb_showinfo(const struct xusb_device *xusb_device) +{ + struct libusb_device_descriptor dev_desc; + struct libusb_device *dev; + const struct xusb_iface **piface; + int ret; + + assert(xusb_device); + dev = xusb_device->impl->dev; + ret = libusb_get_device_descriptor(dev, &dev_desc); + if (ret < 0) { + ERR("%s: usb device without a device descriptor\n", + xusb_device->devpath_tail); + return; + } + if (verbose <= LOG_INFO) { + INFO("%s: [%04X:%04X] [%s / %s / %s]\n", + xusb_device->devpath_tail, + dev_desc.idVendor, + dev_desc.idProduct, + xusb_device->iManufacturer, + xusb_device->iProduct, + xusb_device->iSerialNumber); + } else { + printf("USB Bus/Device: [%03d/%03d] (%s)\n", + xusb_device->bus_num, + xusb_device->device_num, + (xusb_device->impl->handle) ? "open" : "closed"); + printf("USB Spec name: [%s]\n", xusb_device->spec->name); + printf("USB iManufacturer: [%s]\n", xusb_device->iManufacturer); + printf("USB iProduct: [%s]\n", xusb_device->iProduct); + printf("USB iSerialNumber: [%s]\n", xusb_device->iSerialNumber); + piface = (const struct xusb_iface **)xusb_device->interfaces; + for (; *piface; piface++) { + printf("USB Interface[%d]: ep_out=0x%02X ep_in=0x%02X claimed=%d [%s]\n", + (*piface)->interface_num, + (*piface)->ep_out, + (*piface)->ep_in, + (*piface)->is_claimed, + (*piface)->iInterface); + } + } +} + +int xusb_close(struct xusb_device *xusb_device) +{ + if (xusb_device && xusb_device->impl && xusb_device->impl->handle) { + assert(xusb_device->spec); + assert(xusb_device->spec->name); + DBG("%s: Closing device \"%s\"\n", + xusb_device->devpath_tail, + xusb_device->spec->name); + libusb_close(xusb_device->impl->handle); + xusb_device->impl->handle = NULL; + } + return 0; +} + +enum xusb_transfer_type xusb_transfer_type(const struct xusb_iface *iface) +{ + const struct xusb_device *xusb_device; + const struct libusb_interface_descriptor *iface_desc; + const struct libusb_endpoint_descriptor *ep; + enum xusb_transfer_type ret = XUSB_TT_ILLEGAL; + + assert(iface); + xusb_device = iface->xusb_device; + assert(xusb_device); + iface_desc = get_iface_descriptor(xusb_device, iface->interface_num); + assert(iface_desc); + ep = iface_desc->endpoint; + assert(ep); + switch (ep->bmAttributes) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + break; + case LIBUSB_TRANSFER_TYPE_BULK: + ret = XUSB_TT_BULK; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + ret = XUSB_TT_INTERRUPT; + break; + } + return ret; +} + +int xusb_send(struct xusb_iface *iface, const char *buf, int len, int timeout) +{ + struct xusb_device *xusb_device = iface->xusb_device; + int actual_len; + int ep_out = EP_OUT(iface); + int ret; + + if (!xusb_device->impl->handle) { + ERR("%s: device closed\n", xusb_device->devpath_tail); + return -ENXIO; + } + dump_packet(LOG_DEBUG, DBG_MASK, __func__, buf, len); + if (!EP_IS_OUT(ep_out)) { + XUSB_ERR(iface, "%s called with an input endpoint 0x%x\n", + __func__, ep_out); + return -EINVAL; + } + switch (iface->transfer_type) { + case XUSB_TT_BULK: + ret = libusb_bulk_transfer(xusb_device->impl->handle, ep_out, + (unsigned char *)buf, len, &actual_len, timeout); + break; + case XUSB_TT_INTERRUPT: + ret = libusb_interrupt_transfer(xusb_device->impl->handle, ep_out, + (unsigned char *)buf, len, &actual_len, timeout); + break; + default: + ret = -EAFNOSUPPORT; + break; + } + if (ret) { + /* + * If the device was gone, it may be the + * result of renumeration. Ignore it. + */ + if (ret != LIBUSB_ERROR_NO_DEVICE) { + XUSB_ERR(iface, "write to endpoint 0x%x failed: (%d) %s\n", + ep_out, ret, libusb_error_name(ret)); + dump_packet(LOG_ERR, DBG_MASK, "xbus_send[ERR]", + buf, len); + /*exit(2);*/ + } else { + XUSB_DBG(iface, "write to endpoint 0x%x got ENODEV\n", + ep_out); + xusb_close(xusb_device); + } + return errno_map(ret); + } else if (actual_len != len) { + XUSB_ERR(iface, "write to endpoint 0x%x short write: (%d) %s\n", + ep_out, ret, libusb_error_name(ret)); + dump_packet(LOG_ERR, DBG_MASK, "xbus_send[ERR]", buf, len); + return -EFAULT; + } + return actual_len; +} + +int xusb_recv(struct xusb_iface *iface, char *buf, size_t len, int timeout) +{ + struct xusb_device *xusb_device = iface->xusb_device; + int actual_len; + int ep_in = EP_IN(iface); + int ret; + + if (!xusb_device->impl->handle) { + ERR("%s: device closed\n", xusb_device->devpath_tail); + return -ENXIO; + } + if (!EP_IS_IN(ep_in)) { + XUSB_ERR(iface, "%s called with an output endpoint 0x%x\n", + __func__, ep_in); + return -EINVAL; + } + switch (iface->transfer_type) { + case XUSB_TT_BULK: + ret = libusb_bulk_transfer(xusb_device->impl->handle, ep_in, + (unsigned char *)buf, len, &actual_len, timeout); + break; + case XUSB_TT_INTERRUPT: + ret = libusb_interrupt_transfer(xusb_device->impl->handle, ep_in, + (unsigned char *)buf, len, &actual_len, timeout); + break; + default: + ret = -EAFNOSUPPORT; + break; + } + if (ret < 0) { + XUSB_DBG(iface, "read from endpoint 0x%x failed: (%d) %s\n", + ep_in, ret, libusb_error_name(ret)); + memset(buf, 0, len); + return errno_map(ret); + } + dump_packet(LOG_DEBUG, DBG_MASK, __func__, buf, actual_len); + return actual_len; +} diff --git a/xpp/xtalk/xusb_test.c b/xpp/xtalk/xusb_test.c new file mode 100644 index 0000000..8f0b37b --- /dev/null +++ b/xpp/xtalk/xusb_test.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include + +static char *progname; + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D [hexnum ....]\n", + progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, + "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + fprintf(stderr, "\tDevice:\n"); + fprintf(stderr, "\t\t/proc/bus/usb//\n"); + fprintf(stderr, "\t\t/dev/bus/usb//\n"); + exit(1); +} + +static void xusb_destructor(void *data) +{ + struct xusb_device *xusb_device = data; + xusb_destroy(xusb_device); +} + +#define XUSB_IFACE_DESC(p, i, d) \ + { SPEC_HEAD(0xe4e4, (p), (d)), (i) } + +static struct xusb_iface_description { + struct xusb_spec spec; + int interface_num; +} test_specs[] = { + /* 115x */ + XUSB_IFACE_DESC(0x1150, 1, "old-astribank-NOFW"), + XUSB_IFACE_DESC(0x1151, 1, "old-astribank-USB"), + XUSB_IFACE_DESC(0x1152, 1, "old-astribank-FPGA"), + + /* 116x */ + XUSB_IFACE_DESC(0x1160, 1, "astribank2-NOFW"), + XUSB_IFACE_DESC(0x1161, 1, "astribank2-USB"), + XUSB_IFACE_DESC(0x1162, 1, "astribank2-FPGA"), + XUSB_IFACE_DESC(0x1163, 1, "astribank2-TWINSTAR"), + XUSB_IFACE_DESC(0x1191, 1, "xpanel"), + XUSB_IFACE_DESC(0x1183, 1, "multi-ps"), + XUSB_IFACE_DESC(0x11a3, 0, "auth-dongle"), + XUSB_IFACE_DESC(0xbb01, 0, "iwc"), + {}, +}; + +static int run_spec(int i, xusb_filter_t filter, char *devpath) +{ + struct xusb_iface_description *desc = &test_specs[i]; + struct xusb_spec *s = &desc->spec; + struct xlist_node *xlist; + struct xlist_node *curr; + int interface_num = desc->interface_num; + int num_devs; + + if (!s->name) + return 0; + xlist = xusb_find_byproduct(s, 1, filter, devpath); + num_devs = (xlist) ? xlist_length(xlist) : 0; + INFO("total %d devices of type %s (interface=%d)\n", + num_devs, s->name, interface_num); + for (curr = xlist_shift(xlist); curr; curr = xlist_shift(xlist)) { + struct xusb_device *xusb_device; + struct xusb_iface *iface; + int ret; + + xusb_device = curr->data; + ret = xusb_claim(xusb_device, interface_num, &iface); + if (ret < 0) { + ERR("%s: xusb_claim() failed (ret = %d)\n", + xusb_devpath(xusb_device), ret); + continue; + } + xusb_showinfo(xusb_device); + xusb_release(iface); + } + xlist_destroy(xlist, xusb_destructor); + return 1; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + const char options[] = "vd:D:EFp"; + xusb_filter_t filter = NULL; + int i; + + progname = argv[0]; + while (1) { + int c; + + c = getopt(argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + filter = xusb_filter_bypath; + break; + case 'v': + verbose++; + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + i = 0; + while (run_spec(i, filter, devpath)) + i++; + return 0; +} diff --git a/xpp/xtalk/xusb_test_bypath.c b/xpp/xtalk/xusb_test_bypath.c new file mode 100644 index 0000000..8cd4792 --- /dev/null +++ b/xpp/xtalk/xusb_test_bypath.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include + +static char *progname; + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] / ...\n", progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, + "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + exit(1); +} + +static int check_devpath(const char *devpath) +{ + const struct xusb_spec *spec; + struct xusb_device *xusb_dev; + struct xusb_iface *iface; + int ret; + + printf("Checking %s: ", devpath); + fflush(stdout); + xusb_dev = xusb_find_bypath(devpath); + if (!xusb_dev) { + ERR("missing %s\n", devpath); + return 1; + } + spec = xusb_device_spec(xusb_dev); + printf("Found: %04x:%04x\n", spec->vendor_id, spec->product_id); + ret = xusb_claim(xusb_dev, 1, &iface); + if (ret < 0) { + ERR("%s: xusb_claim() failed (ret = %d)\n", + xusb_devpath(xusb_dev), ret); + return 1; + } + xusb_showinfo(xusb_dev); + xusb_release(iface); + return 1; +} + +int main(int argc, char *argv[]) +{ + const char options[] = "vd:"; + int i; + + progname = argv[0]; + while (1) { + int c; + + c = getopt(argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'v': + verbose++; + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + for (i = optind; i < argc; i++) + check_devpath(argv[i]); + return 0; +}