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 <oron.peled@xorcom.com> Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
This commit is contained in:
parent
5ee15a2337
commit
9c61e40187
|
@ -69,4 +69,10 @@ xpp/test_parse
|
||||||
xpp/twinstar.8
|
xpp/twinstar.8
|
||||||
xpp/xpp_blink.8
|
xpp/xpp_blink.8
|
||||||
xpp/xpp_sync.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
|
zonedata.lo
|
||||||
|
|
|
@ -372,6 +372,7 @@ AC_CONFIG_FILES([
|
||||||
hotplug/Makefile
|
hotplug/Makefile
|
||||||
ppp/Makefile
|
ppp/Makefile
|
||||||
xpp/Makefile
|
xpp/Makefile
|
||||||
|
xpp/xtalk/Makefile
|
||||||
xpp/oct612x/Makefile
|
xpp/oct612x/Makefile
|
||||||
xpp/perl_modules/Makefile
|
xpp/perl_modules/Makefile
|
||||||
])
|
])
|
||||||
|
|
|
@ -64,7 +64,7 @@ CLEANFILES = $(perl_checks) $(perl_mans)
|
||||||
|
|
||||||
if PBX_USB
|
if PBX_USB
|
||||||
|
|
||||||
SUBDIRS += oct612x
|
SUBDIRS += oct612x xtalk
|
||||||
|
|
||||||
if LIBUSBX
|
if LIBUSBX
|
||||||
USB_CFLAGS = $(LIBUSBX_CFLAGS)
|
USB_CFLAGS = $(LIBUSBX_CFLAGS)
|
||||||
|
|
|
@ -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)
|
|
@ -27,7 +27,8 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
#include <debug.h>
|
#include <xtalk/debug.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
int verbose = LOG_INFO;
|
int verbose = LOG_INFO;
|
||||||
int debug_mask;
|
int debug_mask;
|
||||||
|
|
|
@ -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 */
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef DEBUG_H
|
||||||
|
#define DEBUG_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2008, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <xtalk/api_defs.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
|
@ -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 */
|
|
@ -0,0 +1,186 @@
|
||||||
|
#ifndef XTALK_PROTO_H
|
||||||
|
#define XTALK_PROTO_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XTALK - Base protocol for our USB devices
|
||||||
|
* It is meant to provide a common base for layered
|
||||||
|
* protocols (dialects)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <xtalk/api_defs.h>
|
||||||
|
#include <xtalk/firmware_defs.h>
|
||||||
|
|
||||||
|
#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 */
|
|
@ -0,0 +1,76 @@
|
||||||
|
#ifndef XTALK_PROTO_RAW_H
|
||||||
|
#define XTALK_PROTO_RAW_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XTALKSYNC - Base synchronous protocol for our USB devices
|
||||||
|
* It is meant to provide a common base for layered
|
||||||
|
* protocols (dialects)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <xtalk/api_defs.h>
|
||||||
|
#include <xtalk/proto.h>
|
||||||
|
#include <xtalk/firmware_defs.h>
|
||||||
|
|
||||||
|
#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 */
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef XTALK_PROTO_SYNC_H
|
||||||
|
#define XTALK_PROTO_SYNC_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xtalk/api_defs.h>
|
||||||
|
#include <xtalk/proto.h>
|
||||||
|
|
||||||
|
#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 <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <xtalk/firmware_defs.h>
|
||||||
|
|
||||||
|
#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 */
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef XLIST_H
|
||||||
|
#define XLIST_H
|
||||||
|
|
||||||
|
#include <xtalk/api_defs.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
struct xlist_node {
|
||||||
|
void *data;
|
||||||
|
struct xlist_node *next;
|
||||||
|
struct xlist_node *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*xlist_destructor_t)(void *data);
|
||||||
|
|
||||||
|
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 */
|
|
@ -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;
|
||||||
|
};
|
|
@ -0,0 +1,115 @@
|
||||||
|
#ifndef XUSB_H
|
||||||
|
#define XUSB_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2008, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <xtalk/api_defs.h>
|
||||||
|
#include <xtalk/xlist.h>
|
||||||
|
|
||||||
|
#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 */
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <xlist.h>
|
#include <xtalk/xlist.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
struct xlist_node *xlist_new(void *data)
|
struct xlist_node *xlist_new(void *data)
|
||||||
{
|
{
|
||||||
|
@ -36,6 +37,17 @@ void xlist_destroy(struct xlist_node *list, xlist_destructor_t destructor)
|
||||||
free(list);
|
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)
|
void xlist_append_item(struct xlist_node *list, struct xlist_node *item)
|
||||||
{
|
{
|
||||||
assert(list);
|
assert(list);
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <xtalk/xlist.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* 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 <assert.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,353 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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);
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef XTALK_BASE_H
|
||||||
|
#define XTALK_BASE_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <xtalk/proto.h>
|
||||||
|
#include <xtalk/firmware_defs.h>
|
||||||
|
|
||||||
|
#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 */
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/proto_raw.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <xtalk/proto_raw.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
|
static char *progname;
|
||||||
|
|
||||||
|
#define DBG_MASK 0x10
|
||||||
|
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [options...] -D <device> [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/<bus>/<dev>\n");
|
||||||
|
fprintf(stderr, "\t\t/dev/bus/usb/<bus>/<dev>\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;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
|
.\" (C) Copyright 2013 Oron Peled <oron@actcom.co.il>,
|
||||||
|
.\"
|
||||||
|
.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 <n> 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 <iface_num>
|
||||||
|
Specify USB interface number (default 1).
|
||||||
|
.TP
|
||||||
|
.B \-t <timeout>
|
||||||
|
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 <mask>
|
||||||
|
Debug mask. Use 0xFF for everything.
|
||||||
|
.SH AUTHOR
|
||||||
|
Oron Peled <oron@actcom.co.il>
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <xtalk/proto_sync.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
|
#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 <busnum>/<devnum> [hexnum ....]\n",
|
||||||
|
progname);
|
||||||
|
fprintf(stderr, "\tOptions:\n");
|
||||||
|
fprintf(stderr,
|
||||||
|
"\t\t[-I<iface_num>] # Interface number (default %d)\n",
|
||||||
|
iface_num);
|
||||||
|
fprintf(stderr,
|
||||||
|
"\t\t[-t<timeout>] # 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;
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2009, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/proto_sync.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <xtalk/proto_sync.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
|
static char *progname;
|
||||||
|
|
||||||
|
#define DBG_MASK 0x10
|
||||||
|
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [options...] -D <device> [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/<bus>/<dev>\n");
|
||||||
|
fprintf(stderr, "\t\t/dev/bus/usb/<bus>/<dev>\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;
|
||||||
|
}
|
|
@ -0,0 +1,381 @@
|
||||||
|
#define _GNU_SOURCE /* for memrchr() */
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
#ifndef XUSB_COMMON_H
|
||||||
|
#define XUSB_COMMON_H
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* 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 <limits.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
|
@ -0,0 +1,843 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* Copyright (C) 2008, Xorcom
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/sem.h>
|
||||||
|
#include <usb.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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, "<BYPATH>",
|
||||||
|
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, "<EMPTY>", 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();
|
||||||
|
}
|
|
@ -0,0 +1,826 @@
|
||||||
|
/*
|
||||||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
#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, "<BYPATH>",
|
||||||
|
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, "<EMPTY>", 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;
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
|
static char *progname;
|
||||||
|
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [options...] -D <device> [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/<bus>/<dev>\n");
|
||||||
|
fprintf(stderr, "\t\t/dev/bus/usb/<bus>/<dev>\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;
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <xtalk/debug.h>
|
||||||
|
#include <xtalk/xusb.h>
|
||||||
|
#include <autoconfig.h>
|
||||||
|
|
||||||
|
static char *progname;
|
||||||
|
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [options...] <busnum>/<devnum> ...\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;
|
||||||
|
}
|
Loading…
Reference in New Issue