diff --git a/configure.ac b/configure.ac index c22dde902..6e332780e 100644 --- a/configure.ac +++ b/configure.ac @@ -1272,6 +1272,103 @@ AC_SUBST(ENABLE_PARPORT) AM_CONDITIONAL(ENABLE_PARPORT, test "${ENABLE_PARPORT}" = "true") +dnl We support both libftdi 1.x and libftdi 0.x +if test "$cross_compiling" != yes; then +AC_CHECK_PROGS(LIBFTDI_CONFIG, libftdi1-config libftdi-config) +else +LIBFTDI_CONFIG="" +fi +AC_SUBST(LIBFTDI_CONFIG) + +AC_ARG_WITH(libftdi-config, + [ --with-libftdi-config=PATH Specify full path to libftdi-config or libftdi1-config]) +dnl Check if user passed a specific libftdi-config program. +if test "X$with_libftdi_config" != "X" ; then + LIBFTDI_CONFIG=$with_libftdi_config +fi + +if test "$cross_compiling" != yes; then + if test "X$LIBFTDI_CONFIG" == "X" ; then + dirs="/usr/bin /usr/local/bin /opt/local/bin" + for i in $dirs; do + for prog in libftdi1-config libftdi-config; do + echo "Testing $i/$prog" + if test -x $i/$prog; then + AC_MSG_RESULT($i/$prog is found) + LIBFTDI_CONFIG="$i/$prog" + break 2; + fi + done + done + fi +fi + +# Check if FTDI should be enabled +ENABLE_FTDI=auto +AC_MSG_CHECKING([if libftdi is available]) + +AC_ARG_ENABLE(ftdi, +[ --enable-ftdi Enable LinkUSB support via libftdi (default auto)], +[ + AC_MSG_RESULT([$enableval]) + + if ! test "$enableval" = "yes" ; then + ENABLE_FTDI=false + fi +], +[ + AC_MSG_RESULT([auto (default)]) +]) + +# Include ftdi.h if enabled +if test "${ENABLE_FTDI}" != "false" ; then + + LIBFTDI_FOUND=false + if test "X${LIBFTDI_CONFIG}" != "X" ; then + LIBFTDI_CFLAGS=`$LIBFTDI_CONFIG --cflags` + LIBFTDI_LIBS=`$LIBFTDI_CONFIG --libs` + + save_CPPFLAGS="$CPPFLAGS" + save_LDFLAGS="$LDFLAGS" + CPPFLAGS="$save_CPPFLAGS $LIBFTDI_CFLAGS" + LDFLAGS="$save_LDFLAGS $LIBFTDI_LIBS" + + AC_MSG_CHECKING([if libftdi compiles with includes+lib ]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[ + struct ftdi_context ftdic; + ftdi_init(&ftdic); + ]])], + [AC_MSG_RESULT([ok]) + FTDI_FOUND=true + ], + [AC_MSG_RESULT([compilation error]) + LIBFTDI_CFLAGS="" + LIBFTDI_LDFLAGS="" + LIBFTDI_CONFIG="" + ]) + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + fi + + if test "${FTDI_FOUND}" = "true"; then + ENABLE_FTDI=true + else + if test "${ENABLE_FTDI}" = "true" ; then + AC_MSG_ERROR([libftdi must be installed to use LinkUSB natively]) + else + AC_MSG_WARN([libftdi not found, LinkUSB native will be disabled]) + ENABLE_FTDI=false + fi + fi +fi +AC_SUBST(LIBFTDI_CFLAGS) +AC_SUBST(LIBFTDI_LIBS) +AC_SUBST(ENABLE_FTDI) +AM_CONDITIONAL(ENABLE_FTDI, test "${ENABLE_FTDI}" = "true") + if test "${HAVE_CYGWIN}" = "true" ; then OW_CYGWIN=1 else @@ -1489,6 +1586,13 @@ else fi AC_SUBST(OW_PARPORT) +if test "${ENABLE_FTDI}" = "true" ; then + OW_FTDI=1 +else + OW_FTDI=0 +fi +AC_SUBST(OW_FTDI) + if test "${ENABLE_DEBUG}" = "true" ; then OW_DEBUG=1 else @@ -1529,7 +1633,7 @@ AC_FUNC_SELECT_ARGTYPES AC_FUNC_STRFTIME AC_FUNC_STRTOD AC_TYPE_SIGNAL -AC_CHECK_FUNCS([accept daemon getaddrinfo freeaddrinfo gethostbyname2_r gethostbyaddr_r gethostbyname_r getservbyname_r getopt getopt_long gettimeofday inet_ntop inet_pton memchr memset select socket strcasecmp strchr strdup strncasecmp strtol strtoul twalk tsearch tfind tdelete tdestroy vasprintf strsep vsprintf vsnprintf writev getline]) +AC_CHECK_FUNCS([accept daemon getaddrinfo freeaddrinfo gethostbyname2_r gethostbyaddr_r gethostbyname_r getservbyname_r getopt getopt_long gmtime_r gettimeofday localtime_r inet_ntop inet_pton memchr memset select socket strcasecmp strchr strdup strncasecmp strtol strtoul twalk tsearch tfind tdelete tdestroy vasprintf strsep vsprintf vsnprintf writev getline]) if test "${ENABLE_ZERO}" = "true" ; then AC_SEARCH_LIBS(dlopen, dl, AC_DEFINE(HAVE_DLOPEN, 1, [Define if you have dlopen])) @@ -1687,6 +1791,11 @@ if test "${ENABLE_PARPORT}" = "true"; then else AC_MSG_RESULT([ Parallel port DS1410E is DISABLED]) fi +if test "${ENABLE_FTDI}" = "true"; then + AC_MSG_RESULT([ FTDI (LinkUSB) is enabled]) +else + AC_MSG_RESULT([ FTDI (LinkUSB) is DISABLED]) +fi if test "${ENABLE_ZERO}" = "true"; then AC_MSG_RESULT([ Zeroconf/Bonjour is enabled]) else diff --git a/module/owlib/src/c/Makefile.am b/module/owlib/src/c/Makefile.am index 65fccc17a..e77a78a84 100644 --- a/module/owlib/src/c/Makefile.am +++ b/module/owlib/src/c/Makefile.am @@ -109,6 +109,7 @@ OWLIB_SOURCE = ow_dl.c \ ow_fakeread.c \ ow_filelength.c \ ow_fstat.c \ + ow_ftdi.c \ ow_generic_read.c \ ow_get.c \ ow_help.c \ @@ -224,7 +225,7 @@ libow_la_SOURCES = ${OWLIB_SOURCE} if HAVE_CYGWIN libow_la_LDFLAGS = ${PTHREAD_LIBS} -shared -no-undefined ${LIBUSB_LIBS} ${LIBAVAHI_LIBS} ${LD_EXTRALIBS} else -libow_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -release $(LT_RELEASE) ${PTHREAD_LIBS} -shared -shrext .so ${LIBUSB_LIBS} ${LIBAVAHI_LIBS} ${LD_EXTRALIBS} +libow_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -release $(LT_RELEASE) ${PTHREAD_LIBS} -shared -shrext .so ${LIBUSB_LIBS} ${LIBFTDI_LIBS} ${LIBAVAHI_LIBS} ${LD_EXTRALIBS} endif # Maybe need this for MacOS X @@ -252,6 +253,7 @@ AM_CFLAGS = -I../include \ ${EXTRACFLAGS} \ ${PTHREAD_CFLAGS} \ ${LIBUSB_CFLAGS} \ + ${LIBFTDI_CFLAGS} \ ${PIC_FLAGS} diff --git a/module/owlib/src/c/ow_arg.c b/module/owlib/src/c/ow_arg.c index 83fcf5538..23c357339 100644 --- a/module/owlib/src/c/ow_arg.c +++ b/module/owlib/src/c/ow_arg.c @@ -18,25 +18,32 @@ #include "ow_connection.h" #include "ow_usb_msg.h" // for DS9490_port_setup -enum arg_address { arg_addr_device, arg_addr_null, arg_addr_ip, arg_addr_colon, arg_addr_number, arg_addr_other, arg_addr_error, } ; +enum arg_address { arg_addr_device, arg_addr_null, + arg_addr_ip, arg_addr_colon, + arg_addr_number, arg_addr_ftdi, + arg_addr_other, arg_addr_error, } ; static enum arg_address ArgType( const char * arg ) { static regex_t rx_dev ; static regex_t rx_num ; static regex_t rx_ip ; + static regex_t rx_ftdi ; static regex_t rx_col ; // compile regex expressions ow_regcomp( &rx_dev, "/", REG_NOSUB ) ; ow_regcomp( &rx_num, "^[:digit:]+$", REG_NOSUB ) ; ow_regcomp( &rx_ip, "[:digit:]{1,3}\\.[:digit:]{1,3}\\.[:digit:]{1,3}\\.[:digit:]{1,3}", REG_NOSUB ) ; + ow_regcomp( &rx_ftdi, "^ftdi:", REG_NOSUB ) ; ow_regcomp( &rx_col, ":", REG_NOSUB ) ; if ( arg == NULL ) { return arg_addr_null ; } else if ( ow_regexec( &rx_ip, arg, NULL ) == 0 ) { return arg_addr_ip ; + } else if ( ow_regexec( &rx_ftdi, arg, NULL ) == 0 ) { + return arg_addr_ftdi; } else if ( ow_regexec( &rx_col, arg, NULL ) == 0 ) { return arg_addr_colon ; } else if ( ow_regexec( &rx_dev, arg, NULL ) == 0 ) { @@ -62,6 +69,7 @@ static void arg_data( const char * arg, struct port_in * pin ) // Test whether address is a serial port, or a serial over telnet (ser2net) static GOOD_OR_BAD Serial_or_telnet( const char * arg, struct connection_in * in ) { + LEVEL_DEBUG("arg %s is %d", arg, ArgType(arg)); switch( ArgType(arg) ) { case arg_addr_null: case arg_addr_error: @@ -70,6 +78,14 @@ static GOOD_OR_BAD Serial_or_telnet( const char * arg, struct connection_in * in case arg_addr_device: in->pown->type = ct_serial ; // serial port break ; + case arg_addr_ftdi: +#if OW_FTDI + in->pown->type = ct_ftdi; + break; +#else + LEVEL_DEFAULT("FTDI support not included in compilation. Use generic serial device."); + return gbBAD; +#endif case arg_addr_number: // port case arg_addr_colon: case arg_addr_ip: @@ -87,7 +103,8 @@ GOOD_OR_BAD ARG_Device(const char *arg) switch( ArgType(arg) ) { case arg_addr_number: // port case arg_addr_colon: - case arg_addr_ip: + case arg_addr_ip: + case arg_addr_ftdi: case arg_addr_other: return ARG_Serial(arg) ; default: diff --git a/module/owlib/src/c/ow_com.c b/module/owlib/src/c/ow_com.c index 40a375f6c..0db57554a 100644 --- a/module/owlib/src/c/ow_com.c +++ b/module/owlib/src/c/ow_com.c @@ -12,6 +12,7 @@ #include "owfs_config.h" #include "ow.h" #include "ow_connection.h" +#include "ow_ftdi.h" #ifdef HAVE_LINUX_LIMITS_H #include @@ -38,6 +39,7 @@ GOOD_OR_BAD COM_test( struct connection_in * connection ) LEVEL_DEBUG("Unimplemented!!!"); return gbBAD ; case ct_serial: + case ct_ftdi: break ; } @@ -72,6 +74,11 @@ void COM_flush( const struct connection_in *connection) return ; case ct_serial: tcflush( connection->pown->file_descriptor, TCIOFLUSH); + return; + case ct_ftdi: +#if OW_FTDI + owftdi_flush(connection); +#endif break ; } } @@ -103,6 +110,11 @@ void COM_break(struct connection_in *in) return ; case ct_serial: tcsendbreak(in->pown->file_descriptor, 0); + return; + case ct_ftdi: +#if OW_FTDI + owftdi_break(in); +#endif break ; } } diff --git a/module/owlib/src/c/ow_com_change.c b/module/owlib/src/c/ow_com_change.c index 9cc399151..9d3b29c6c 100644 --- a/module/owlib/src/c/ow_com_change.c +++ b/module/owlib/src/c/ow_com_change.c @@ -47,6 +47,10 @@ GOOD_OR_BAD COM_change( struct connection_in *connection) return gbGOOD ; case ct_serial: return serial_change( connection ) ; + case ct_ftdi: +#if OW_FTDI + return owftdi_change( connection ) ; +#endif case ct_unknown: case ct_none: default: @@ -77,6 +81,7 @@ void COM_set_standard( struct connection_in *connection) break ; case ct_serial: + case ct_ftdi: default: pin->timeout.tv_sec = Globals.timeout_serial ; pin->timeout.tv_usec = 0 ; diff --git a/module/owlib/src/c/ow_com_close.c b/module/owlib/src/c/ow_com_close.c index 03ee2acc2..9a9f927fc 100644 --- a/module/owlib/src/c/ow_com_close.c +++ b/module/owlib/src/c/ow_com_close.c @@ -13,6 +13,7 @@ #include "owfs_config.h" #include "ow.h" #include "ow_connection.h" +#include "ow_ftdi.h" #ifdef HAVE_LINUX_LIMITS_H #include @@ -43,6 +44,11 @@ void COM_close(struct connection_in *connection) return ; case ct_serial: break ; + case ct_ftdi: +#if OW_FTDI + return owftdi_close(connection); +#endif + break; } switch ( pin->state ) { diff --git a/module/owlib/src/c/ow_com_free.c b/module/owlib/src/c/ow_com_free.c index bd586610d..a5d2847b0 100644 --- a/module/owlib/src/c/ow_com_free.c +++ b/module/owlib/src/c/ow_com_free.c @@ -13,6 +13,7 @@ #include "owfs_config.h" #include "ow.h" #include "ow_connection.h" +#include "ow_ftdi.h" #ifdef HAVE_LINUX_LIMITS_H #include @@ -44,6 +45,11 @@ void COM_free(struct connection_in *connection) case ct_serial: serial_free( connection ) ; break ; + case ct_ftdi: +#if OW_FTDI + owftdi_free( connection ) ; +#endif + break ; } connection->pown->state = cs_virgin ; diff --git a/module/owlib/src/c/ow_com_open.c b/module/owlib/src/c/ow_com_open.c index d3d83c4a4..19572f064 100644 --- a/module/owlib/src/c/ow_com_open.c +++ b/module/owlib/src/c/ow_com_open.c @@ -13,6 +13,7 @@ #include "ow.h" #include "ow_connection.h" #include "ow_w1.h" +#include "ow_ftdi.h" #ifdef HAVE_LINUX_LIMITS_H #include @@ -64,6 +65,10 @@ GOOD_OR_BAD COM_open(struct connection_in *connection) return gbBAD ; case ct_serial: return serial_open( head_in ) ; + case ct_ftdi: +#if OW_FTDI + return owftdi_open( head_in ) ; +#endif /* OW_FTDI */ case ct_unknown: case ct_none: default: diff --git a/module/owlib/src/c/ow_com_read.c b/module/owlib/src/c/ow_com_read.c index 577443808..5e9eda5be 100644 --- a/module/owlib/src/c/ow_com_read.c +++ b/module/owlib/src/c/ow_com_read.c @@ -71,6 +71,13 @@ GOOD_OR_BAD COM_read( BYTE * data, size_t length, struct connection_in *connecti } break ; } + case ct_ftdi: + { +#if OW_FTDI + size_t actual = owftdi_read(data, length, connection); + return actual == (ssize_t) length ? gbGOOD : gbBAD ; +#endif + } } return gbBAD ; } diff --git a/module/owlib/src/c/ow_com_write.c b/module/owlib/src/c/ow_com_write.c index 60d7d1a29..f6967e24b 100644 --- a/module/owlib/src/c/ow_com_write.c +++ b/module/owlib/src/c/ow_com_write.c @@ -53,6 +53,7 @@ GOOD_OR_BAD COM_write( const BYTE * data, size_t length, struct connection_in *c case ct_tcp: case ct_serial: case ct_netlink: + case ct_ftdi: // These protocols need no special pre-processing break ; } @@ -64,7 +65,15 @@ GOOD_OR_BAD COM_write( const BYTE * data, size_t length, struct connection_in *c } // try the write - RETURN_GOOD_IF_GOOD( COM_write_once( data, length, connection ) ); + switch ( pin->type ) { + case ct_ftdi: +#if OW_FTDI + RETURN_GOOD_IF_GOOD( owftdi_write_once( data, length, connection ) ); + break; +#endif + default: + RETURN_GOOD_IF_GOOD( COM_write_once( data, length, connection ) ); + } LEVEL_DEBUG("Trouble writing to %s", SAFESTRING(DEVICENAME(connection)) ) ; @@ -75,7 +84,14 @@ GOOD_OR_BAD COM_write( const BYTE * data, size_t length, struct connection_in *c LEVEL_DEBUG("Reopened %s, now slurp", SAFESTRING(DEVICENAME(connection)) ) ; COM_slurp(connection) ; LEVEL_DEBUG("Write again to %s", SAFESTRING(DEVICENAME(connection)) ) ; - return COM_write_once( data, length, connection ) ; + switch ( pin->type ) { + case ct_ftdi: +#if OW_FTDI + return owftdi_write_once( data, length, connection ); +#endif + default: + return COM_write_once( data, length, connection ); + } } // Can't open or another type of error @@ -111,6 +127,11 @@ GOOD_OR_BAD COM_write_simple( const BYTE * data, size_t length, struct connectio case ct_serial: case ct_netlink: break ; + case ct_ftdi: +#if OW_FTDI + return owftdi_write_once(data, length, connection); +#endif + break; } if ( pin->file_descriptor == FILE_DESCRIPTOR_BAD ) { diff --git a/module/owlib/src/c/ow_connect.c b/module/owlib/src/c/ow_connect.c index 64b86faf2..0626bc2c9 100644 --- a/module/owlib/src/c/ow_connect.c +++ b/module/owlib/src/c/ow_connect.c @@ -208,6 +208,10 @@ void RemoveIn( struct connection_in * conn ) return ; } + /* Close master-specific resources. This may do writes + * and what-not that expects the conn to be kept setup. */ + BUS_close(conn) ; + // owning port pin = conn->pown ; @@ -242,8 +246,8 @@ void RemoveIn( struct connection_in * conn ) _MUTEX_DESTROY(conn->dev_mutex); SAFETDESTROY( conn->dev_db, owfree_func); - /* Close master-specific resources */ - BUS_close(conn) ; + /* Free port */ + COM_free( conn ) ; /* Next free up internal resources */ SAFEFREE( DEVICENAME(conn) ) ; @@ -259,9 +263,6 @@ void RemovePort( struct port_in * pin ) return ; } - /* Free port */ - COM_free( pin->first ) ; - /* First delete connections */ while ( pin->first != NO_CONNECTION ) { RemoveIn( pin->first ) ; diff --git a/module/owlib/src/c/ow_ftdi.c b/module/owlib/src/c/ow_ftdi.c new file mode 100644 index 000000000..954821390 --- /dev/null +++ b/module/owlib/src/c/ow_ftdi.c @@ -0,0 +1,535 @@ +/* + OWFS -- One-Wire filesystem + ow_ftdi: low-level communication routines for FTDI-based adapters + + Copyright 2014-2016 Johan Ström + Released under GPLv2 for inclusion in the OWFS project. + + This file implements a serial layer for use with FTDI-based USB serial + adapters. It should be usable as an in-place replacement for any ports + with type ct_serial, given that the actual device can be reached through + a FTDI chip (which is accessible). + + The main benifit of using this rather than accessing through a regular + ttyUSB/cuaU device is that it will tweak the FTDI chip's latency timer. + Since 1W transactions often use single character reads, the default setting + inflicts a 16ms delay on every single read operation. With this layer + we bring that down to (max) 1ms. + + This is (mostly) called from ow_com* routines, when the underlying + port_in type is ct_ftdi. + + Please note that this is only a serial layer, for interacting with FTDI + serial adapter chips. Generally, there is no way to know WHAT is conneted + a given chip. Thus, ut is *not* possible to write a "scan for any FTDI + 1wire device" feature, unless the device has a unique VID/PID. + + The owftdi_open method assumes that the device arg is prefixed with "ftdi:". + + The following args can be used to identify a specific port: + + ftdi:d: + path of bus and device-node (e.g. "003/001") within usb device tree + (usually at /proc/bus/usb/) + ftdi:i:: + first device with given vendor and product id, ids can be decimal, octal + (preceded by "0") or hex (preceded by "0x") + ftdi:i::: + as above with index being the number of the device (starting with 0) + if there are more than one + ftdi:s::: + first device with given vendor id, product id and serial string + + The strings are passed to libftdi as a "descriptor". + An additional format is supported, for certain bus types. + + ftdi: + + This only works if there is a pre-defined VID/PID for the bus type used. + Currently, this is only valid for the VID/PID found on the LinkUSB (i.e. bus_link). + Note that those VID/PID's are the default for any FT232R device, and in no way exclusive + to LinkUSB. +*/ + +#include +#include "owfs_config.h" +#include "ow_connection.h" + +#if OW_FTDI +//#define BENCH + +#define FTDI_LINKUSB_VID 0x0403 +#define FTDI_LINKUSB_PID 0x6001 + +#include +#include +#include + +#include "ow_ftdi.h" + +#define FTDIC(in) ((in)->master.link.ftdic) +static GOOD_OR_BAD owftdi_open_device(struct connection_in *in, const char *description) ; +static GOOD_OR_BAD owftdi_open_device_specific(struct connection_in *in, int vid, int pid, const char *serial) ; +static GOOD_OR_BAD owftdi_opened(struct connection_in *in) ; + +#define owftdi_configure_baud(in) owftdi_configure_baud0(in, COM_BaudRate((in)->pown->baud)) +static GOOD_OR_BAD owftdi_configure_baud0(struct connection_in *in, int baud) ; +static GOOD_OR_BAD owftdi_configure_bits(struct connection_in *in, enum ftdi_break_type break_) ; +static GOOD_OR_BAD owftdi_configure_dtrrts(struct connection_in *in, int value) ; + +GOOD_OR_BAD owftdi_open( struct connection_in * in ) { + GOOD_OR_BAD gbResult = gbBAD; + const char* arg = in->pown->init_data; + const char* target = arg+5; + assert(arg); + assert(strstr(arg, "ftdi:") == arg); + + if(strlen(target) < 2) { + LEVEL_DEFAULT("Invalid ftdi target string"); + return gbBAD ; + } + + char c0 = target[0]; + char c1 = target[1]; + const char* serial = NULL; + if(c0 != 'd' && c0 != 'i' && c0 != 's' && c1 != ':') { + // Assume plain serial; only works with known device-types + serial = target; + } + + if(FTDIC(in)) { + LEVEL_DEFAULT("FTDI interface %s was NOT closed?", + DEVICENAME(in)); + return gbBAD ; + } + + if(!(FTDIC(in) = ftdi_new())) { + LEVEL_DEFAULT("Failed to get FTDI context"); + return gbBAD ; + } + + if(serial) { + switch(in->pown->busmode) { + case bus_link: + gbResult = owftdi_open_device_specific(in, + FTDI_LINKUSB_VID, FTDI_LINKUSB_PID, + serial); + + if(GOOD(gbResult)) + in->adapter_name = "LinkUSB"; + + break; + default: + LEVEL_DEFAULT( + "Cannot use ftdi: with this device (%d), please use full format", + in->pown->busmode); + gbResult = gbBAD; + break; + } + }else{ + gbResult = owftdi_open_device(in, target); + } + + if(GOOD(gbResult)) { + gbResult = owftdi_change(in); + } + + if(BAD(gbResult)) { + ftdi_free(FTDIC(in)); + FTDIC(in) = NULL; + } + else { + in->pown->state = cs_deflowered; + } + + + + return gbResult; +} + +void owftdi_free( struct connection_in *in ) { + if(!FTDIC(in)) { + assert(in->pown->file_descriptor == FILE_DESCRIPTOR_BAD); + return ; + } + + // Best effort, port might not be open, only context + ftdi_usb_close(FTDIC(in)); + + ftdi_free(FTDIC(in)); + FTDIC(in) = NULL; + + in->pown->state = cs_virgin; + // TODO: Cleaner? + in->pown->file_descriptor = FILE_DESCRIPTOR_BAD; +} + +void owftdi_close( struct connection_in * in ) { + owftdi_free(in); +} + +void owftdi_flush( const struct connection_in *in ) { + if(!FTDIC(in)) { + LEVEL_DEFAULT("Cannot flush FTDI interface %s, not open", DEVICENAME(in)); + return; + } + + ftdi_usb_purge_tx_buffer(FTDIC(in)); + ftdi_usb_purge_rx_buffer(FTDIC(in)); +} + + +void owftdi_break ( struct connection_in *in ) { + assert (FTDIC(in)); + + /* tcsendbreak manpage: + * linux: 0.25-0.5s + * freebsd: "four tenths of a second" + * + * break for 0.4s here as well + */ + LEVEL_DEBUG("Sending FTDI break.."); + owftdi_configure_bits(in, BREAK_ON); + usleep(400000); + owftdi_configure_bits(in, BREAK_OFF); +} + + +static GOOD_OR_BAD owftdi_open_device(struct connection_in *in, const char *description) { + int ret = ftdi_usb_open_string(FTDIC(in), description); + if(ret != 0) { + ERROR_CONNECT("Failed to open FTDI device with description '%s': %d = %s", + description, ret, ftdi_get_error_string(FTDIC(in))); + return ret; + } + + return owftdi_opened(in); +} + +static GOOD_OR_BAD owftdi_open_device_specific(struct connection_in *in, int vid, int pid, const char *serial) { + int ret = ftdi_usb_open_desc(FTDIC(in), vid, pid, NULL, serial); + if(ret != 0) { + ERROR_CONNECT("Failed to open FTDI device with vid/pid 0x%x/0%x and serial '%s': %d = %s", + vid, pid, serial, ret, ftdi_get_error_string(FTDIC(in))); + return ret; + } + + return owftdi_opened(in); +} + +static GOOD_OR_BAD owftdi_opened(struct connection_in *in) { + int ret; + + /* Set DTR and RTS high. This is normally done by the OS when using the + * generic serial interface. We must do it manually, or we will not be able to + * communicate with the LinkUSB after boot/re-plug. + * + * Unfortunately, this can not be used to power-cycle the LinkUSB, it seems.. + */ + RETURN_BAD_IF_BAD(owftdi_configure_dtrrts(in, 1)); + + // Set latency timer to 1ms; improves response speed alot + if((ret = ftdi_set_latency_timer(FTDIC(in), 1)) != 0) { + ERROR_CONNECT("Failed to set FTDI latency timer: %d = %s", + ret, + ftdi_get_error_string(FTDIC(in))); + return gbBAD; + } + + // Fake a file_descriptor, so we can pass FILE_DESCRIPTOR_NOT_VALID check in + // COM_read, COM_test + // TODO: Cleaner? + in->pown->file_descriptor = 999; + + return gbGOOD; +} + +static GOOD_OR_BAD owftdi_configure_baud0(struct connection_in *in, int baud) { + int ret; + if((ret = ftdi_set_baudrate(FTDIC(in), baud)) != 0) { + ERROR_CONNECT("Failed to set FTDI baud rate to %d: %d = %s", + baud, + ret, + ftdi_get_error_string(FTDIC(in))); + return gbBAD; + } + + return gbGOOD; +} + +static GOOD_OR_BAD owftdi_configure_bits(struct connection_in *in, enum ftdi_break_type break_) { + struct port_in * pin = in->pown ; + enum ftdi_bits_type bits; + enum ftdi_stopbits_type stop; + enum ftdi_parity_type parity; + int ret; + + switch (pin->bits) { + case 7: + bits = BITS_7; + break ; + case 8: + default: + bits = BITS_8; + break ; + } + + switch (pin->parity) { + case parity_none: + parity = NONE; + break ; + case parity_even: + parity = EVEN; + break ; + case parity_odd: + parity = ODD; + break ; + case parity_mark: + parity = MARK; + break ; + } + + // stop bits + switch (pin->stop) { + case stop_1: + stop = STOP_BIT_1; + break ; + case stop_15: + stop = STOP_BIT_15; + break; + case stop_2: + stop = STOP_BIT_2; + break ; + } + + if((ret = ftdi_set_line_property2(FTDIC(in), bits, stop, parity, break_)) != 0) { + ERROR_CONNECT("Failed to set FTDI bit-configuration: %d = %s", + ret, + ftdi_get_error_string(FTDIC(in))); + return gbBAD; + } + + return gbGOOD; +} + +static GOOD_OR_BAD owftdi_configure_flow(struct connection_in *in) { + struct port_in * pin = in->pown ; + int flow; + int ret; + + switch( pin->flow ) { + case flow_hard: + flow = SIO_RTS_CTS_HS; + break ; + case flow_none: + flow = SIO_DISABLE_FLOW_CTRL; + break ; + case flow_soft: + default: + LEVEL_DEBUG("Unsupported COM port flow control"); + return -ENOTSUP ; + } + + if((ret = ftdi_setflowctrl(FTDIC(in), flow)) != 0) { + ERROR_CONNECT("Failed to set FTDI flow-control: %d = %s", + ret, + ftdi_get_error_string(FTDIC(in))); + return gbBAD; + } + + return gbGOOD; +} + +static GOOD_OR_BAD owftdi_configure_dtrrts(struct connection_in *in, int value) { + int ret = ftdi_setdtr_rts(FTDIC(in), value, value); + if(ret != 0) { + ERROR_CONNECT("Failed to set FTDI DTR/RTS high, %d: %s", + ret, ftdi_get_error_string(FTDIC(in))); + return gbBAD; + } + return gbGOOD; +} + +GOOD_OR_BAD owftdi_change(struct connection_in *in) { + RETURN_BAD_IF_BAD(owftdi_configure_baud(in)); + RETURN_BAD_IF_BAD(owftdi_configure_bits(in, BREAK_OFF)); + RETURN_BAD_IF_BAD(owftdi_configure_flow(in)); + + return gbGOOD; +} + +SIZE_OR_ERROR owftdi_read(BYTE * data, size_t requested_size, struct connection_in *in) { + struct port_in * pin = in->pown ; + struct timeval tv_start; + size_t chars_read =0, + to_be_read = requested_size; + int retries = 0; + time_t timeout; + +#ifdef BENCH + struct timeval tv_end, tv_timing; +#endif + + /* Configure USB *block transfer* timeout, this is not wait-for-any-data timeout + * which we must implement manually below. */ + timeout = pin->timeout.tv_sec * 1000 + pin->timeout.tv_usec/1000; + FTDIC(in)->usb_read_timeout = timeout; + + + /* ftdi_read_data will loop internally and read until requested_size has been filled, + * or at least filled from whats available in the FTDI buffer. + * The FTDI buffer is however one step away from the LINK, a RX buffer is inbetween. + * A copy operation from the RX buffer to the FTDI buffer is done only when it's full, + * or when the "latency timer" kicks in. We thus have to keep reading from the FTDI + * buffer for a while. + * + * With our latency timer of 1ms, we will have a empty read after 1ms. However, in most + * cases we actuall DO get data in time! + */ + LEVEL_DEBUG("attempt %"PRIu64" bytes Time: "TVformat, requested_size, TVvar(&(pin->timeout))); + gettimeofday(&tv_start, NULL); + while(to_be_read> 0) { + int ret = ftdi_read_data(FTDIC(in), &data[chars_read], to_be_read); + if(ret < 0) { + LEVEL_DATA("FTDI read failed: %d = %s", + ret, ftdi_get_error_string(FTDIC(in))); + STAT_ADD1(NET_read_errors); + owftdi_close(in); + return -EINVAL; + } + if(ret == 0) { + // No data there yet... Sleep a bit and wait for more + struct timeval tv_cur; + time_t timeleft; + gettimeofday(&tv_cur, NULL); + timeleft = timeout*1000L - + ((tv_cur.tv_sec - tv_start.tv_sec)*1000000L + + (tv_cur.tv_usec - tv_start.tv_usec)); + +#if 0 + LEVEL_DEBUG("No data available, delaying a bit (%"PRIu64"ms left, retrie %d)", + timeleft/1000L, + retries + ); +#endif + + if(timeleft < 0) { + LEVEL_CONNECT("TIMEOUT after %d bytes", requested_size - to_be_read); + // XXX: Stats here is not in tcp_read? + STAT_ADD1_BUS(e_bus_timeouts, in); + return -EAGAIN; + } + + /* In <= 1ms, the latency timer will trigger a copy of the RX buffer + * to the USB bus. When that time has come, we can try to read again. + * Do it a bit quicker to get smaller latency. + * Testing shows that we usually get our data within one or two loops + * in normal situations, so this isn't really heavy busyloading. + */ + usleep(MIN(timeleft, (retries < 10) ? 200 : 1000)); + retries++; + continue; + } + TrafficIn("read", &data[chars_read], ret, in) ; + to_be_read -= ret; + chars_read += ret; + } +#ifdef BENCH + gettimeofday(&tv_end, NULL); + timersub(&tv_end, &tv_start, &tv_timing); + LEVEL_DEBUG("ftdi_read, read %d bytes. %d read-loops=%.6fus, actual= %d", + requested_size, + retries, + ((uint64_t)(tv_timing.tv_sec*1000000L) + (double)(tv_timing.tv_usec)), + chars_read + ); +#endif + + LEVEL_DEBUG("ftdi_read: %d - %d = %d (%d retries)", + (int)requested_size, (int) to_be_read, (int) (requested_size-to_be_read), + retries) ; + return chars_read; +} + +GOOD_OR_BAD owftdi_write_once( const BYTE * data, size_t length, struct connection_in *in) { + // Mimics COM_write_once + + FTDIC(in)->usb_write_timeout = Globals.timeout_serial * 1000; // XXX why not pin->timeout? + + TrafficOut("write", data, length, in); + int ret = ftdi_write_data(FTDIC(in), data, (int)length); + if(ret < 0) { + ERROR_CONNECT("FTDI write to %s failed: %d = %s", + SAFESTRING(DEVICENAME(in)), + ret, + ftdi_get_error_string(FTDIC(in))); + + STAT_ADD1_BUS(e_bus_write_errors, in); + owftdi_close(in); + return gbBAD; + } + + LEVEL_DEBUG("ftdi_write: %"PRIu64" = actual %d", length, ret) ; + + return gbGOOD; +} + +/* slurp up any pending chars -- used at the start to clear the com buffer */ +void owftdi_slurp(struct connection_in *in, uint64_t usec) { + int ret; + BYTE data[1]; + + /* This function is implemented similar to read; it must do repeated + * ftdi_read_data calls, or we might miss to slurp critical data, + * and the state machine will become confused. + * + * First purge any data in rx buffer, then do some subsequent reads. + */ + + ret = ftdi_usb_purge_rx_buffer(FTDIC(in)); + if(ret != 0) { + ERROR_CONNECT("Failed to purge rx buffers on FTDI device, %d: %s", + ret, ftdi_get_error_string(FTDIC(in))); + } + + // Allow for at least 2 rounds of latency timeouts.. or we seem to miss \n in 19200 sometimes.. + usec = MAX(usec, 2*1000); + + // USB block transfer timeout + int prev_timeout = FTDIC(in)->usb_read_timeout; + struct timeval tv_start; + FTDIC(in)->usb_read_timeout = usec/1000; + + gettimeofday(&tv_start, NULL); + while(1) { + ret = ftdi_read_data(FTDIC(in), data, 1); +#if 0 + LEVEL_DEBUG("ftdi_slurp read ret %d, %s ", ret, ftdi_get_error_string(FTDIC(in))); +#endif + if(ret < 1) { + // No data there yet... Sleep a bit and wait for more + struct timeval tv_cur; + time_t timeleft; + gettimeofday(&tv_cur, NULL); + timeleft = usec - + ((tv_cur.tv_sec - tv_start.tv_sec)*1000000L + + (tv_cur.tv_usec - tv_start.tv_usec)); + + if(timeleft < 0) { +#if 0 + LEVEL_DEBUG("ftdi_slurp timeout, timeleft=%d ", timeleft); +#endif + return; + } + + usleep(MIN(timeleft, 200)); + continue; + } + + TrafficIn("slurp", data, 1, in); + } + + FTDIC(in)->usb_read_timeout = prev_timeout; +} + +#endif /* OW_FTDI */ diff --git a/module/owlib/src/c/ow_link.c b/module/owlib/src/c/ow_link.c index a6483459f..85cbbd6bd 100644 --- a/module/owlib/src/c/ow_link.c +++ b/module/owlib/src/c/ow_link.c @@ -118,6 +118,7 @@ struct LINK_id LINK_id_tbl[] = { {"VM12", "LINK OEM v1.2", adapter_LINK_12}, {"1.3", "LinkUSB V1.3", adapter_LINK_13}, {"1.4", "LinkUSB V1.4", adapter_LINK_14}, + {"1.5", "LinkUSB V1.5", adapter_LINK_15}, {"0", "0", 0} }; @@ -136,6 +137,7 @@ static void LINKE_setroutines(struct connection_in *in); static RESET_TYPE LINK_reset_in(struct connection_in * in); static GOOD_OR_BAD LINK_detect_serial(struct connection_in * in) ; +static GOOD_OR_BAD LINK_detect_serial0(struct connection_in * in, int timeout_secs) ; static GOOD_OR_BAD LINK_detect_net(struct connection_in * in) ; static GOOD_OR_BAD LINK_version(struct connection_in * in) ; @@ -256,58 +258,125 @@ GOOD_OR_BAD LINK_detect(struct port_in *pin) case ct_serial: - pin->baud = B9600 ; - - pin->flow = flow_first ; - RETURN_GOOD_IF_GOOD( LINK_detect_serial(in) ) ; - - LEVEL_DEBUG("Second attempt at serial LINK setup"); - pin->flow = flow_second ; - RETURN_GOOD_IF_GOOD( LINK_detect_serial(in) ) ; + case ct_ftdi: + { + /* The LINK (both regular and USB) can run in different baud modes. + * On power reset it will always use 9600bps, but it can be configured for other + * baud rates by sending different commands. + * For ct_serial links we use 9600bps, but for the improved ct_ftdi we + * go ahead and improve the speed as well, trying to keep it at 19200bps. + * + * The Link DOES support higher baud rates (38400, 56700), BUT then we have + * to enforce output throttling in order to not overflow the 1-Wire bus... + * So, keep it at 19200. + * + * To avoid trouble, let the older serial devices use 9600 for now... + */ + RETURN_BAD_IF_BAD(LINK_detect_serial(in)); + +#if 0 + // Note! This has not been tested with anything but LinkUSB 1.5 + if(pin->type != ct_ftdi) + return gbGOOD; +#endif - LEVEL_DEBUG("Third attempt at serial LINK setup"); - pin->flow = flow_first ; - RETURN_GOOD_IF_GOOD( LINK_detect_serial(in) ) ; + LEVEL_DEBUG("Reconfiguring found LinkUSB to 19200bps"); + pin->baud = B19200; + LINK_set_baud(in); - LEVEL_DEBUG("Fourth attempt at serial LINK setup"); - pin->flow = flow_second ; - RETURN_GOOD_IF_GOOD( LINK_detect_serial(in) ) ; - break ; + // ensure still found + RETURN_GOOD_IF_GOOD( LINK_version(in) ) ; + ERROR_CONNECT("LINK baud reconfigure failed, cannot find it after 19200 change."); + break; + } default: return gbBAD ; } return gbBAD ; } +/* Detect serial device by probing with different baud-rates. Calls LINK_detect_serial0 */ +static GOOD_OR_BAD LINK_detect_serial(struct connection_in * in) { + /* LINK docs does not say anything about flow control. Studying the LinkOEM + * datasheet however, indicates that we only have Tx and Rx; no RTS/CTS. + * And it does not mention XON/XOFF.. + * And it works fine with no flow control... So, let's go with no flow control. + * + * For startup detection, we first try with 9600, then 19200. We do however + * force a shorter timeout, the standard 5s is pretty overkill.. + */ + int i; + struct port_in * pin = in->pown ; + speed_t bauds[] = {B9600, B19200, B38400 +#ifdef B57600 +, B57600 +#endif + , 0}; +#define SHORT_TIMEOUT 1 + + /* A break should put the Link in 9600... + * However, the LinkUSB v1.5 fails to do this, unless we are in byte mode. + * If we are, it reacts to break, and changes to 9600.. + * But, in plain command mode, break does not work. + * + * iButtonLink said they would investige this bug(?), but never replied. + */ + COM_break( in ) ; + + i=0; + // First try quickly in all baudrates with no flow control + while((pin->baud = bauds[i++]) != 0) { + pin->flow = flow_none; + LEVEL_DEBUG("Detecting %s LINK using %d bps", + (pin->type == ct_serial ? "serial" : "ftdi"), + COM_BaudRate(pin->baud)); + RETURN_GOOD_IF_GOOD( LINK_detect_serial0(in, SHORT_TIMEOUT) ) ; + } + + i=0; + // No? Try different flow controls then (the way it was done in the old code..) + while((pin->baud = bauds[i++]) != 0) { + LEVEL_DEBUG("Second attempt at serial LINK setup, %d bps", COM_BaudRate(pin->baud)); + pin->flow = flow_first; + RETURN_GOOD_IF_GOOD( LINK_detect_serial0(in, Globals.timeout_serial) ) ; + + LEVEL_DEBUG("Third attempt at serial LINK setup"); + pin->flow = flow_second ; + RETURN_GOOD_IF_GOOD( LINK_detect_serial0(in, Globals.timeout_serial) ) ; + } + + return gbBAD; +} -static GOOD_OR_BAD LINK_detect_serial(struct connection_in * in) +/* Detect serial device with a specific configured baud-rate */ +static GOOD_OR_BAD LINK_detect_serial0(struct connection_in * in, int timeout_secs) { struct port_in * pin = in->pown ; /* Set up low-level routines */ LINK_setroutines(in); - pin->timeout.tv_sec = Globals.timeout_serial ; + pin->timeout.tv_sec = timeout_secs ; pin->timeout.tv_usec = 0 ; /* Open the com port */ RETURN_BAD_IF_BAD(COM_open(in)) ; - - //COM_break( in ) ; + + COM_break( in ) ; // BREAK also sends it into command mode, if in another mode LEVEL_DEBUG("Slurp in initial bytes"); LINK_slurp( in ) ; - UT_delay(100) ; // based on http://morpheus.wcf.net/phpbb2/viewtopic.php?t=89&sid=3ab680415917a0ebb1ef020bdc6903ad + UT_delay(100) ; // based on https://web.archive.org/web/20080624060114/http://morpheus.wcf.net/phpbb2/viewtopic.php?t=89 LINK_slurp( in ) ; RETURN_GOOD_IF_GOOD( LINK_version(in) ) ; - LEVEL_DEFAULT("LINK detection error"); + LEVEL_DEFAULT("LINK detection error, trying powercycle"); serial_powercycle(in) ; LEVEL_DEBUG("Slurp in initial bytes"); LINK_slurp( in ) ; - UT_delay(100) ; // based on http://morpheus.wcf.net/phpbb2/viewtopic.php?t=89&sid=3ab680415917a0ebb1ef020bdc6903ad + UT_delay(100) ; // based on https://web.archive.org/web/20080624060114/http://morpheus.wcf.net/phpbb2/viewtopic.php?t=89 LINK_slurp( in ) ; RETURN_GOOD_IF_GOOD( LINK_version(in) ) ; - LEVEL_DEFAULT("LINK detection error"); + LEVEL_DEFAULT("LINK detection error, giving up"); COM_close(in) ; return gbBAD; } @@ -322,7 +391,7 @@ static GOOD_OR_BAD LINK_detect_net(struct connection_in * in) /* Open the tcp port */ RETURN_BAD_IF_BAD( COM_open(in) ) ; - + LEVEL_DEBUG("Slurp in initial bytes"); // LINK_slurp( in ) ; UT_delay(1000) ; // based on http://morpheus.wcf.net/phpbb2/viewtopic.php?t=89&sid=3ab680415917a0ebb1ef020bdc6903ad @@ -422,6 +491,7 @@ static GOOD_OR_BAD LINK_version(struct connection_in * in) static void LINK_set_baud(struct connection_in * in) { struct port_in * pin = in->pown ; + int send_break = 0; char * speed_code ; if ( pin->type == ct_telnet ) { @@ -431,13 +501,14 @@ static void LINK_set_baud(struct connection_in * in) COM_BaudRestrict( &(pin->baud), B9600, B19200, B38400, B57600, 0 ) ; - LEVEL_DEBUG("to %d",COM_BaudRate(pin->baud)); + LEVEL_DEBUG("LINK set baud to %d",COM_BaudRate(pin->baud)); // Find rate parameter switch ( pin->baud ) { case B9600: - COM_break(in) ; - LINK_flush(in); - return ; + // Put in Byte mode, and then send break. + speed_code = "b"; + send_break = 1; + break; case B19200: speed_code = "," ; break ; @@ -464,6 +535,9 @@ static void LINK_set_baud(struct connection_in * in) return ; } + if(send_break) { + COM_break(in); + } // Send configuration change LINK_flush(in); @@ -780,10 +854,21 @@ GOOD_OR_BAD LINK_aux_read(int *level_out, struct connection_in * in) { } -static void LINK_close(struct connection_in *in) -{ +static void LINK_close(struct connection_in *in) { + struct port_in * pin = in->pown ; + if ( pin->state == cs_virgin ) { + LEVEL_DEBUG("LINK_close called on already closed connection"); + return ; + } + + if(pin->baud != B9600) { + // Ensure we reset it to 9600bps to speed up detection on next startup + LEVEL_DEBUG("Reconfiguring adapeter to 9600bps before closing"); + pin->baud = B9600; + LINK_set_baud(in); + } + // the standard COM_free routine cleans up the connection - (void) in ; } static GOOD_OR_BAD LINK_PowerByte(const BYTE data, BYTE * resp, const UINT delay, const struct parsedname *pn) diff --git a/module/owlib/src/c/ow_slurp.c b/module/owlib/src/c/ow_slurp.c index a48778986..d815fd9ed 100644 --- a/module/owlib/src/c/ow_slurp.c +++ b/module/owlib/src/c/ow_slurp.c @@ -50,6 +50,7 @@ void COM_slurp( struct connection_in * connection ) { LEVEL_DEBUG("Unimplemented"); return ; case ct_serial: + case ct_ftdi: usec = 100000 ; // timeout increased for slurp based on Johan Strom's recommendation break ; } @@ -58,6 +59,13 @@ void COM_slurp( struct connection_in * connection ) { return ; } + if( connection->pown->type == ct_ftdi ) { +#if OW_FTDI + owftdi_slurp(connection, 1000); +#endif + return; + } + Slurp( connection->pown->file_descriptor, usec ) ; } diff --git a/module/owlib/src/include/ow_connection_in.h b/module/owlib/src/include/ow_connection_in.h index 0b9ecc75c..2c45f5995 100644 --- a/module/owlib/src/include/ow_connection_in.h +++ b/module/owlib/src/include/ow_connection_in.h @@ -52,6 +52,7 @@ enum adapter_type { adapter_LINK_12, adapter_LINK_13, adapter_LINK_14, + adapter_LINK_15, adapter_LINK_other, adapter_LINK_E, adapter_masterhub, diff --git a/module/owlib/src/include/ow_ftdi.h b/module/owlib/src/include/ow_ftdi.h new file mode 100644 index 000000000..ec4c17063 --- /dev/null +++ b/module/owlib/src/include/ow_ftdi.h @@ -0,0 +1,27 @@ +/** + * ow_ftdi lowlevel communication routines + */ + +#if OW_FTDI + +#ifndef OW_FTDI_H +#define OW_FTDI_H + +// for some local types +#include "ow_localreturns.h" + +struct connection_in ; + +GOOD_OR_BAD owftdi_open( struct connection_in * in ) ; +void owftdi_close( struct connection_in * in ) ; +GOOD_OR_BAD owftdi_change(struct connection_in *in) ; +void owftdi_free( struct connection_in *in ) ; +void owftdi_flush( const struct connection_in *in ) ; +void owftdi_break( struct connection_in *in ) ; +SIZE_OR_ERROR owftdi_read(BYTE * data, size_t length, struct connection_in *in) ; +GOOD_OR_BAD owftdi_write( const BYTE * data, size_t length, struct connection_in *connection) ; + +#endif /* OW_FTDI_H */ + +#endif /* OW_FTDI */ + diff --git a/module/owlib/src/include/ow_master.h b/module/owlib/src/include/ow_master.h index 9ddc5b32c..602692d85 100644 --- a/module/owlib/src/include/ow_master.h +++ b/module/owlib/src/include/ow_master.h @@ -122,6 +122,9 @@ enum e_link_t_mode { e_link_t_unknown, e_link_t_extra, e_link_t_none } ; struct master_link { enum e_link_t_mode tmode ; // extra ',' before tF0 command enum e_link_t_mode qmode ; //extra '?' after b command +#if OW_FTDI + struct ftdi_context *ftdic; +#endif }; // Embedded Data Systems HA5 hub diff --git a/module/owlib/src/include/ow_port_in.h b/module/owlib/src/include/ow_port_in.h index 4ce00c77d..7e2f7f98e 100644 --- a/module/owlib/src/include/ow_port_in.h +++ b/module/owlib/src/include/ow_port_in.h @@ -48,6 +48,7 @@ enum bus_mode { bus_tester, bus_mock, bus_link, + bus_linkusb, bus_masterhub, bus_pbm, bus_etherweather, @@ -73,6 +74,7 @@ enum com_type { ct_i2c, ct_usb, ct_netlink, + ct_ftdi, ct_none, } ; diff --git a/src/include/owfs_config.h.in b/src/include/owfs_config.h.in index adadcd838..d04db4e02 100644 --- a/src/include/owfs_config.h.in +++ b/src/include/owfs_config.h.in @@ -61,6 +61,7 @@ #define OW_USB @OW_USB@ #define OW_AVAHI @OW_AVAHI@ #define OW_PARPORT @OW_PARPORT@ +#define OW_FTDI @OW_FTDI@ #define OW_DEBUG @OW_DEBUG@ #define OW_MUTEX_DEBUG @OW_MUTEX_DEBUG@ #define OW_W1 @OW_W1@ diff --git a/src/man/man1/device.1so b/src/man/man1/device.1so index 8bfae4a82..01b569c69 100644 --- a/src/man/man1/device.1so +++ b/src/man/man1/device.1so @@ -19,6 +19,12 @@ Linux and BSD enforce a security policy restricting access to hardware ports. Yo .I port specifies a serial port, e.g. .I /dev/ttyS0 +.P +If OWFS was built with \fBlibftdi\fR support, you may be able to use the +.I ftdi: +prefix to address a FTDI-based USB device. +.br +For details, see the FTDI ADDRESSING section. .TP \fI-d port\fI \fR|\fR \fI--device=port\fI \fB(DS2480B)\fB DS2480B-based bus master (like the DS9097U or the LINK in emulation mode). If the adapter doesn't respond, a passive type (DS9907E or diode/resistor) circuit will be assumed. @@ -54,6 +60,8 @@ Reverse polarity of the DS2480B output transistors? Not needed for the DS9097U, .B iButtonLink .I LINK adapter (all versions) in non-emulation mode. Uses an ascii protocol over serial. +.br +This supports the simplified \fIftdi:\fB\fR addressing scheme. .TP \fI--ha7e=port\fI \fB(HA7E)\fB .B Embedded Data Systems @@ -217,3 +225,35 @@ Use the linux kernel w1 virtual bus master. .I \-\-timeout_w1=10 Timeout for w1 netlink communications. This has a 10 second default and can be changed dynamically under .I /settings/timeout/w1 +.SH "FTDI ADDRESSING" +FTDI is a brand of USB-to-serial chips which are very common. If your serial device is connected via a USB serial dongle based on a FTDI chip, or if your +adapter uses a built-in FTDI USB chip (for example, the LinkUSB), you can use this FTDI addressing. +.P +The main benifit with this mode of access is that we can decrease the communication delay, yielding twice as fast 1-Wire communication in many cases. +.P +The following values for \fIport\fR can be used to identify a specific FTDI port. Note that this requires that OWFS is built with libftdi support. +.TP +\fIftdi:d:\fB\fB +path of bus and device-node (e.g. "003/001") within usb device tree +(usually at /proc/bus/usb/) +.TP +\fIftdi:i:\fI\fB:\fB +first device with given vendor and product id, ids can be decimal, octal +(preceded by "0") or hex (preceded by "0x") +.TP +\fIftdi:i:\fI\fB::\fB +as above with index being the number of the device (starting with 0) +if there are more than one +.TP +\fIftdi:s:\fI\fB::\fB +first device with given vendor id, product id and serial string +.P +The above formats are parsed fully by libftdi (minus the \fIftdi:\fR prefix). +.SS Simplified device \fBserial-only\fB support +An additional format is supported, for certain bus types. This only specifies the USB serial number. +.TP +\fIftdi:\fI\fB\fB +Identifies a FTDI device by serial only. +Currently, this is only valid for the VID/PID found on the LinkUSB (i.e. --link). +Note that those VID/PID's are the default for any FT232R device, and in no way exclusive +to LinkUSB.