Browse Source

Merge branch 'bfx2' into bfgminer

Conflicts:
	README
Luke Dashjr 11 years ago
parent
commit
117e9f7059
10 changed files with 579 additions and 38 deletions
  1. 9 1
      Makefile.am
  2. 1 0
      README
  3. 42 1
      configure.ac
  4. 319 0
      driver-bfx.c
  5. 1 1
      driver-x6500.c
  6. 1 1
      jtag.c
  7. 194 18
      lowl-ftdi.c
  8. 10 14
      lowl-ftdi.h
  9. 1 1
      lowlevel.c
  10. 1 1
      lowlevel.h

+ 9 - 1
Makefile.am

@@ -255,7 +255,7 @@ bfgminer_SOURCES += driver-modminer.c
 endif
 
 if HAS_X6500
-bfgminer_SOURCES += driver-x6500.c ft232r.c ft232r.h jtag.c jtag.h
+bfgminer_SOURCES += driver-x6500.c jtag.c jtag.h
 endif
 
 if HAS_ZTEX
@@ -273,6 +273,10 @@ if HAS_BFSB
 bfgminer_SOURCES += driver-bfsb.c
 endif
 
+if USE_BFX
+bfgminer_SOURCES += driver-bfx.c
+endif
+
 if HAS_METABANK
 bfgminer_SOURCES += driver-metabank.c tm_i2c.h tm_i2c.c
 endif
@@ -299,6 +303,10 @@ if USE_HASHFAST
 bfgminer_SOURCES += driver-hashfast.c
 endif
 
+if NEED_BFG_LOWL_FTDI
+bfgminer_SOURCES += lowl-ftdi.c lowl-ftdi.h
+endif
+
 if NEED_BFG_LOWL_HID
 bfgminer_SOURCES += lowl-hid.c lowl-hid.h
 bfgminer_CPPFLAGS += $(hidapi_CFLAGS)

+ 1 - 0
README

@@ -138,6 +138,7 @@ BFGMiner driver configuration options:
 	                        enabled
 	--disable-avalon        Compile support for Avalon (default enabled)
 	--enable-bfsb           Compile support for BFSB (default disabled)
+	--disable-bfx           Compile support for BFx2 (default if libusb)
 	--disable-bifury        Compile support for Bi*Fury (default enabled)
 	--disable-bigpic        Compile support for Big Picture Mining USB (default
 	                        enabled)

+ 42 - 1
configure.ac

@@ -124,6 +124,7 @@ need_binloader=no
 need_dynclock=no
 need_lowl_vcom=no
 need_lowlevel=no
+need_lowl_ftdi=no
 need_lowl_hid=no
 need_lowl_pci=no
 need_lowl_usb=no
@@ -663,7 +664,7 @@ fi
 if test "x$x6500" = xyes; then
 	AC_DEFINE([USE_X6500], [1], [Defined to 1 if X6500 support is wanted])
 	need_dynclock=yes
-	need_lowl_usb=yes
+	need_lowl_ftdi=yes
 	need_binloader=yes
 	has_fpga=yes
 	have_udevrules=true
@@ -764,6 +765,39 @@ fi
 AM_CONDITIONAL([HAS_BIGPIC], [test x$bigpic = xyes])
 
 
+driverlist="$driverlist bfx"
+AC_ARG_ENABLE([bfx],
+	[AC_HELP_STRING([--disable-bfx],[Compile support for BFx2 (default if libusb)])],
+	[bfx=$enableval],
+	[bfx=$ddauto]
+	)
+if test "x$bfx" = "xno"; then
+	true
+elif test "x$bitfury$libusb" = "xyesyes"; then
+	bfx=yes
+elif test "x$bfx" = "xyes"; then
+	if test "x$want_libusb" = "xno"; then
+		AC_MSG_ERROR([You disabled libusb, required for BFx2 support])
+	elif test "x$libusb" = "xno"; then
+		AC_MSG_ERROR([Could not find libusb, required for BFx2 support])
+	else
+		AC_MSG_ERROR([You explicitly disabled Bitfury and explicitly enabled BFx2])
+	fi
+elif test "x$bfx" = "xauto"; then
+	bfx="$libusb"
+	if test "x$libusb" = xno; then
+		AC_MSG_WARN([Could not find libusb, required for BFx2 support])
+		bfx_enableaction="install libusb 1.0+"
+	fi
+fi
+if test "x$bfx" = xyes; then
+	AC_DEFINE([USE_BFX], [1], [Defined to 1 if BFx2 support is wanted])
+	need_lowl_ftdi=yes
+	has_asic=yes
+fi
+AM_CONDITIONAL([USE_BFX], [test x$bfx = xyes])
+
+
 driverlist="$driverlist drillbit"
 AC_ARG_ENABLE([drillbit],
 	[AC_HELP_STRING([--disable-drillbit],[Compile support for DrillBit (default enabled)])],
@@ -977,6 +1011,12 @@ if test "x$need_lowl_vcom" != "xno"; then
 	fi
 fi
 
+lowllist="$lowllist ftdi/need_lowl_ftdi"
+if test x$need_lowl_ftdi = xyes; then
+	AC_DEFINE([NEED_BFG_LOWL_FTDI], [1], [Defined to 1 if lowlevel ftdi drivers are being used])
+	need_lowl_usb=yes
+fi
+
 if test "x$need_lowl_usb" = "xno"; then
 	libusb=no
 	LIBUSB_LIBS=''
@@ -1258,6 +1298,7 @@ AM_CONDITIONAL([NEED_LIBBLKMAKER], [test x$with_system_libblkmaker != xyes])
 AM_CONDITIONAL([NEED_BFG_BINLOADER], [test x$need_binloader = xyes])
 AM_CONDITIONAL([NEED_DYNCLOCK], [test x$need_dynclock = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_VCOM], [test x$need_lowl_vcom = xyes])
+AM_CONDITIONAL([NEED_BFG_LOWL_FTDI], [test x$need_lowl_ftdi = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_HID], [test x$need_lowl_hid = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_PCI], [test x$need_lowl_pci = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWLEVEL], [test x$need_lowlevel = xyes])

+ 319 - 0
driver-bfx.c

@@ -0,0 +1,319 @@
+/*
+ * Copyright 2013-2014 Luke Dashjr
+ *
+ * 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 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "deviceapi.h"
+#include "driver-bitfury.h"
+#include "libbitfury.h"
+#include "logging.h"
+#include "lowlevel.h"
+#include "lowl-ftdi.h"
+#include "miner.h"
+#include "util.h"
+
+#define BFX_ADBUS_DIRECTIONS  0xfb
+#define BFX_ACBUS_DIRECTIONS  0xff
+
+BFG_REGISTER_DRIVER(bfx_drv)
+static const struct bfg_set_device_definition bfx_set_device_funcs[];
+
+struct bfx_state {
+	struct lowlevel_device_info *lowl_info;
+	struct ft232r_device_handle *ftdi;
+};
+
+static
+struct ft232r_device_handle *bfx_open(const struct lowlevel_device_info * const info)
+{
+	struct ft232r_device_handle * const ftdi = ft232h_open_mpsse(info);
+	if (!ftdi)
+		applogr(NULL, LOG_ERR, "%s: Failed to open", __func__);
+	if (!ft232h_mpsse_set_adbus(ftdi, 8, BFX_ADBUS_DIRECTIONS))
+	{
+		applog(LOG_ERR, "%s: Failed to set A%cBUS pins", __func__, 'D');
+		goto err;
+	}
+	if (!ft232h_mpsse_set_acbus(ftdi, 0, BFX_ACBUS_DIRECTIONS))
+	{
+		applog(LOG_ERR, "%s: Failed to set A%cBUS pins", __func__, 'C');
+		goto err;
+	}
+	if (!ft232r_purge_buffers(ftdi, FTDI_PURGE_BOTH))
+		applog(LOG_WARNING, "%s: Failed to purge buffers", __func__);
+	
+	return ftdi;
+
+err:
+	ft232h_mpsse_set_adbus(ftdi, 0, 0);
+	ft232h_mpsse_set_acbus(ftdi, 0, 0);
+	ft232r_close(ftdi);
+	return NULL;
+}
+
+static
+void bfx_device_off(struct ft232r_device_handle * const ftdi)
+{
+	// Try to reset everything back to input
+	ft232h_mpsse_set_adbus(ftdi, 0, 0);
+	ft232h_mpsse_set_acbus(ftdi, 0, 0);
+}
+
+static
+bool bfx_set_cs(struct ft232r_device_handle * const ftdi, bool high)
+{
+	const uint8_t val = high ? 8 : 0;
+	return ft232h_mpsse_set_adbus(ftdi, val, BFX_ADBUS_DIRECTIONS);
+}
+
+static
+bool bfx_spi_reset(struct ft232r_device_handle * const ftdi)
+{
+	uint8_t buf[0x10] = { 0xff };
+	for (int i = 1; i < sizeof(buf); ++i)
+		buf[i] = ~buf[i - 1];
+	
+	bfx_set_cs(ftdi, true);
+	ft232r_write_all(ftdi, buf, sizeof(buf));
+	bfx_set_cs(ftdi, false);
+	ft232r_purge_buffers(ftdi, FTDI_PURGE_BOTH);
+	
+	return true;
+}
+
+static
+bool bfx_spi_txrx(struct spi_port * const port)
+{
+	struct cgpu_info * const cgpu = port->cgpu;
+	struct bfx_state * const state = port->userp;
+	struct ft232r_device_handle * const ftdi = state->ftdi;
+	const void *wrbuf = spi_gettxbuf(port);
+	void *rdbuf = spi_getrxbuf(port);
+	size_t bufsz = spi_getbufsz(port);
+	
+	bfx_spi_reset(ftdi);
+	if (ft232h_mpsse_readwrite_all(ftdi, rdbuf, wrbuf, bufsz) != bufsz)
+		goto err;
+	
+	return true;
+
+err:
+	bfx_device_off(ftdi);
+	if (cgpu)
+	{
+		struct thr_info * const thr = cgpu->thr[0];
+		hashes_done2(thr, -1, NULL);
+	}
+	return false;
+}
+
+static
+bool bfx_lowl_probe(const struct lowlevel_device_info * const info)
+{
+	const char * const product = info->product;
+	const char * const serial = info->serial;
+	struct ft232r_device_handle *ftdi;
+	struct spi_port *port;
+	struct bfx_state *state;
+	int chips;
+	
+	if (info->lowl != &lowl_ft232r)
+	{
+		if (info->lowl != &lowl_usb)
+			applog(LOG_DEBUG, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not ft232r!",
+			       __func__, product, serial);
+		return false;
+	}
+	
+	ftdi = bfx_open(info);
+	if (!ftdi)
+		return false;
+	
+	state = malloc(sizeof(*state));
+	*state = (struct bfx_state){
+		.ftdi = ftdi,
+	};
+	port = calloc(1, sizeof(*port));
+	port->userp = state;
+	port->txrx = bfx_spi_txrx;
+	port->repr = bfx_drv.dname;
+	port->logprio = LOG_DEBUG;
+	
+	{
+		struct bitfury_device dummy_bitfury = {
+			.spi = port,
+		};
+		drv_set_defaults(&bfx_drv, bitfury_set_device_funcs_probe, &dummy_bitfury, NULL, NULL, 1);
+	}
+	
+	chips = libbitfury_detectChips1(port);
+	free(port);
+	
+	bfx_device_off(ftdi);
+	ft232r_close(ftdi);
+	
+	if (!chips)
+	{
+		free(state);
+		applog(LOG_DEBUG, "%s: 0 chips detected", __func__);
+		return false;
+	}
+	
+	if (lowlevel_claim(&bfx_drv, true, info))
+	{
+		free(state);
+		return false;
+	}
+	
+	state->lowl_info = lowlevel_ref(info);
+	
+	struct cgpu_info *cgpu;
+	cgpu = malloc(sizeof(*cgpu));
+	*cgpu = (struct cgpu_info){
+		.drv = &bfx_drv,
+		.set_device_funcs = bfx_set_device_funcs,
+		.device_data = state,
+		.threads = 1,
+		.procs = chips,
+		// TODO: .name
+		.dev_manufacturer = maybe_strdup(info->manufacturer),
+		.dev_product = maybe_strdup(product),
+		.dev_serial = maybe_strdup(serial),
+		.deven = DEV_ENABLED,
+		// TODO: .cutofftemp
+	};
+
+	return add_cgpu(cgpu);
+}
+
+static
+bool bfx_init(struct thr_info * const thr)
+{
+	struct cgpu_info * const cgpu = thr->cgpu, *proc;
+	struct bfx_state * const state = cgpu->device_data;
+	struct lowlevel_device_info * const info = state->lowl_info;
+	struct spi_port *port;
+	struct bitfury_device *bitfury;
+	struct ft232r_device_handle *ftdi;
+	
+	ftdi = bfx_open(info);
+	lowlevel_devinfo_free(info);
+	if (!ftdi)
+	{
+		applog(LOG_ERR, "%"PRIpreprv": Failed to open ft232r device", cgpu->proc_repr);
+		return false;
+	}
+	
+	port = malloc(sizeof(*port));
+	bitfury = malloc(sizeof(*bitfury) * cgpu->procs);
+	
+	if (!(port && bitfury && state))
+	{
+		applog(LOG_ERR, "%"PRIpreprv": Failed to allocate structures", cgpu->proc_repr);
+		free(port);
+		free(bitfury);
+		free(state);
+		ft232r_close(ftdi);
+		return false;
+	}
+	
+	/* Be careful, read spidevc.h comments for warnings */
+	memset(port, 0, sizeof(*port));
+	port->txrx = bfx_spi_txrx;
+	port->cgpu = cgpu;
+	port->repr = cgpu->proc_repr;
+	port->logprio = LOG_ERR;
+	
+	state->ftdi = ftdi;
+	port->userp = state;
+	for (proc = cgpu; proc; (proc = proc->next_proc), ++bitfury)
+	{
+		struct thr_info * const mythr = proc->thr[0];
+		*bitfury = (struct bitfury_device){
+			.spi = port,
+			.fasync = proc->proc_id,
+		};
+		proc->device_data = bitfury;
+		mythr->cgpu_data = state;
+		bitfury->osc6_bits = 50;
+		bitfury_send_reinit(bitfury->spi, bitfury->slot, bitfury->fasync, bitfury->osc6_bits);
+		bitfury_init_chip(proc);
+		proc->status = LIFE_INIT2;
+	}
+	
+	timer_set_now(&thr->tv_poll);
+	return true;
+}
+
+static
+void bfx_disable(struct thr_info * const thr)
+{
+	struct bfx_state * const state = thr->cgpu_data;
+	struct ft232r_device_handle * const ftdi = state->ftdi;
+	
+	bitfury_disable(thr);
+	bfx_device_off(ftdi);
+}
+
+static
+void bfx_reinit(struct cgpu_info * const cgpu)
+{
+	struct thr_info * const thr = cgpu->thr[0];
+	struct bfx_state * const state = thr->cgpu_data;
+	struct ft232r_device_handle * const ftdi = state->ftdi;
+	
+	bfx_device_off(ftdi);
+	cgsleep_ms(1);
+	bitfury_enable(thr);
+}
+
+static
+void bfx_shutdown(struct thr_info * const thr)
+{
+	struct bfx_state * const state = thr->cgpu_data;
+	struct ft232r_device_handle * const ftdi = state->ftdi;
+	
+	if (ftdi)
+		bfx_device_off(ftdi);
+}
+
+static const struct bfg_set_device_definition bfx_set_device_funcs[] = {
+	{"osc6_bits", bitfury_set_osc6_bits, "range 1-"BITFURY_MAX_OSC6_BITS_S" (slow to fast)"},
+	{NULL},
+};
+
+struct device_drv bfx_drv = {
+	.dname = "bfx",
+	.name = "BFX",
+	.lowl_probe = bfx_lowl_probe,
+	
+	.thread_init = bfx_init,
+	.thread_disable = bfx_disable,
+	.thread_enable = bitfury_enable,
+	.reinit_device = bfx_reinit,
+	.thread_shutdown = bfx_shutdown,
+	
+	.minerloop = minerloop_async,
+	.job_prepare = bitfury_job_prepare,
+	.job_start = bitfury_noop_job_start,
+	.poll = bitfury_do_io,
+	.job_process_results = bitfury_job_process_results,
+	
+	.get_api_extra_device_detail = bitfury_api_device_detail,
+	.get_api_extra_device_status = bitfury_api_device_status,
+	
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = bitfury_wlogprint_status,
+	.proc_tui_wlogprint_choices = bitfury_tui_wlogprint_choices,
+	.proc_tui_handle_choice = bitfury_tui_handle_choice,
+#endif
+};

+ 1 - 1
driver-x6500.c

@@ -25,8 +25,8 @@
 #include "jtag.h"
 #include "logging.h"
 #include "miner.h"
-#include "ft232r.h"
 #include "lowlevel.h"
+#include "lowl-ftdi.h"
 #include "lowl-usb.h"
 
 #define X6500_USB_PRODUCT "X6500 FPGA Miner"

+ 1 - 1
jtag.c

@@ -16,7 +16,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "ft232r.h"
+#include "lowl-ftdi.h"
 #include "jtag.h"
 #include "logging.h"
 #include "miner.h"

+ 194 - 18
ft232r.c → lowl-ftdi.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012-2013 Luke Dashjr
+ * Copyright 2012-2014 Luke Dashjr
  *
  * 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
@@ -17,13 +17,16 @@
 #include <libusb.h>
 
 #include "compat.h"
-#include "ft232r.h"
 #include "logging.h"
 #include "lowlevel.h"
+#include "lowl-ftdi.h"
 #include "miner.h"
 
 #define FT232R_IDVENDOR   0x0403
 #define FT232R_IDPRODUCT  0x6001
+#define FT232H_IDPRODUCT  0x6014
+
+#define FT232H_LATENCY_MS  2
 
 static
 void ft232r_devinfo_free(struct lowlevel_device_info * const info)
@@ -55,6 +58,7 @@ struct lowlevel_device_info *ft232r_devinfo_scan()
 {
 	struct lowlevel_device_info *devinfo_list = NULL;
 	
+	lowlevel_detect_id(_ft232r_devinfo_scan_cb, &devinfo_list, &lowl_usb, FT232R_IDVENDOR, FT232H_IDPRODUCT);
 	lowlevel_detect_id(_ft232r_devinfo_scan_cb, &devinfo_list, &lowl_usb, FT232R_IDVENDOR, FT232R_IDPRODUCT);
 	
 	return devinfo_list;
@@ -66,33 +70,47 @@ struct lowlevel_device_info *ft232r_devinfo_scan()
 
 #define FTDI_REQUEST_RESET           0
 #define FTDI_REQUEST_SET_BAUDRATE    3
+#define FTDI_REQUEST_SET_EVENT_CHAR     0x06
+#define FTDI_REQUEST_SET_ERROR_CHAR     0x07
+#define FTDI_REQUEST_SET_LATENCY_TIMER  0x09
 #define FTDI_REQUEST_SET_BITMODE  0x0b
 #define FTDI_REQUEST_GET_PINS     0x0c
 #define FTDI_REQUEST_GET_BITMODE  0x0c
 
+#define FTDI_RESET_SIO  0
+
 #define FTDI_BAUDRATE_3M  0,0
 
+#define FTDI_BITMODE_MPSSE  0x02
+
 #define FTDI_INDEX       1
 #define FTDI_TIMEOUT  1000
 
+// http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
+#define FTDI_ADBUS_SET         0x80
+#define FTDI_ACBUS_SET         0x82
+#define FTDI_LOOPBACK_DISABLE  0x85
+#define FTDI_TCK_DIVISOR       0x86
+// Divide-by-five clock prescaler
+#define FTDI_DIV5_ENABLE       0x8b
+
 struct ft232r_device_handle {
 	libusb_device_handle *h;
 	uint8_t i;
 	uint8_t o;
-	unsigned char ibuf[256];
+	int iPktSz;
+	unsigned char ibuf[0x400];
 	int ibufLen;
 	uint16_t osz;
 	unsigned char *obuf;
 	uint16_t obufsz;
+	bool mpsse;
 };
 
-struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *info)
+static
+struct ft232r_device_handle *ftdi_common_open(const struct lowlevel_device_info * const info)
 {
 	libusb_device * const dev = info->lowl_data;
-	info->lowl_data = NULL;
-	
-	if (!dev)
-		return NULL;
 	
 	// FIXME: Cleanup on errors
 	libusb_device_handle *devh;
@@ -112,10 +130,6 @@ struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *info)
 		applog(LOG_ERR, "ft232r_open: Error claiming interface");
 		return NULL;
 	}
-	if (libusb_control_transfer(devh, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_BAUDRATE, FTDI_BAUDRATE_3M, NULL, 0, FTDI_TIMEOUT) < 0) {
-		applog(LOG_ERR, "ft232r_open: Error performing control transfer");
-		return NULL;
-	}
 
 	struct libusb_config_descriptor *cfg;
 	if (libusb_get_config_descriptor(dev, 0, &cfg)) {
@@ -130,6 +144,7 @@ struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *info)
 	ftdi = calloc(1, sizeof(*ftdi));
 	ftdi->h = devh;
 	ftdi->i = altcfg->endpoint[0].bEndpointAddress;
+	ftdi->iPktSz = altcfg->endpoint[0].wMaxPacketSize;
 	ftdi->o = altcfg->endpoint[1].bEndpointAddress;
 	ftdi->osz = 0x1000;
 	ftdi->obuf = malloc(ftdi->osz);
@@ -138,6 +153,99 @@ struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *info)
 	return ftdi;
 }
 
+struct ft232r_device_handle *ft232r_open(const struct lowlevel_device_info * const info)
+{
+	struct ft232r_device_handle * const ftdi = ftdi_common_open(info);
+	if (!ftdi)
+		return NULL;
+	
+	if (libusb_control_transfer(ftdi->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_BAUDRATE, FTDI_BAUDRATE_3M, NULL, 0, FTDI_TIMEOUT) < 0) {
+		applog(LOG_ERR, "ft232r_open: Error performing control transfer");
+		ft232r_close(ftdi);
+		return NULL;
+	}
+	
+	return ftdi;
+}
+
+static
+void ft232h_mpsse_clock_divisor(uint8_t * const buf, const unsigned long clock, const unsigned long freq)
+{
+	const uint16_t divisor = (clock / freq / 2) - 1;
+	buf[0] = divisor & 0xff;
+	buf[1] = divisor >> 8;
+}
+
+static ssize_t ft232r_readwrite(struct ft232r_device_handle *, unsigned char, void *, size_t);
+
+struct ft232r_device_handle *ft232h_open_mpsse(const struct lowlevel_device_info * const info)
+{
+	if (info->pid != FT232H_IDPRODUCT)
+		return NULL;
+	
+	struct ft232r_device_handle * const ftdi = ftdi_common_open(info);
+	uint8_t buf[3];
+	if (!ftdi)
+		return NULL;
+	
+	if (libusb_control_transfer(ftdi->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_RESET, FTDI_RESET_SIO, 1, NULL, 0, FTDI_TIMEOUT) < 0)
+	{
+		applog(LOG_ERR, "%s: Error requesting %s", __func__, "SIO reset");
+		goto err;
+	}
+	
+	if (libusb_control_transfer(ftdi->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_LATENCY_TIMER, FT232H_LATENCY_MS, 1, NULL, 0, FTDI_TIMEOUT) < 0)
+	{
+		applog(LOG_ERR, "%s: Error setting %s", __func__, "latency timer");
+		goto err;
+	}
+	
+	if (libusb_control_transfer(ftdi->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_EVENT_CHAR, 0, 1, NULL, 0, FTDI_TIMEOUT) < 0)
+	{
+		applog(LOG_ERR, "%s: Error setting %s", __func__, "event char");
+		goto err;
+	}
+	
+	if (libusb_control_transfer(ftdi->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_ERROR_CHAR, 0, 1, NULL, 0, FTDI_TIMEOUT) < 0)
+	{
+		applog(LOG_ERR, "%s: Error setting %s", __func__, "error char");
+		goto err;
+	}
+	
+	if (!ft232r_set_bitmode(ftdi, 0, FTDI_BITMODE_MPSSE))
+	{
+		applog(LOG_ERR, "%s: Error setting %s", __func__, "MPSSE bitmode");
+		goto err;
+	}
+	
+	buf[0] = FTDI_DIV5_ENABLE;
+	if (ft232r_readwrite(ftdi, ftdi->o, buf, 1) != 1)
+	{
+		applog(LOG_ERR, "%s: Error requesting %s", __func__, "divide-by-five clock prescaler");
+		goto err;
+	}
+	
+	buf[0] = FTDI_TCK_DIVISOR;
+	ft232h_mpsse_clock_divisor(&buf[1], 12000000, 200000);
+	if (ft232r_readwrite(ftdi, ftdi->o, buf, 3) != 3)
+	{
+		applog(LOG_ERR, "%s: Error setting %s", __func__, "MPSSE clock divisor");
+		goto err;
+	}
+	
+	buf[0] = FTDI_LOOPBACK_DISABLE;
+	if (ft232r_readwrite(ftdi, ftdi->o, buf, 1) != 1)
+		applog(LOG_WARNING, "%s: Error disabling loopback", __func__);
+	
+	ftdi->mpsse = true;
+	
+	return ftdi;
+
+err:
+	ft232r_close(ftdi);
+	return NULL;
+}
+
 void ft232r_close(struct ft232r_device_handle *dev)
 {
 	libusb_release_interface(dev->h, 0);
@@ -179,7 +287,18 @@ static ssize_t ft232r_readwrite(struct ft232r_device_handle *dev, unsigned char
 				errno = ETIMEDOUT;
 				return -1;
 			}
+			// fallthru
 		case 0:
+			if (opt_dev_protocol)
+			{
+				char x[(transferred * 2) + 1];
+				bin2hex(x, data, transferred);
+				applog(LOG_DEBUG, "ft232r %p: %s: %s",
+				       dev,
+				       (endpoint & LIBUSB_ENDPOINT_IN) ? "RECV" : "SEND",
+				       x);
+			}
+			
 			return transferred;
 		default:
 			errno = EIO;
@@ -201,7 +320,7 @@ ssize_t ft232r_flush(struct ft232r_device_handle *dev)
 	return r;
 }
 
-ssize_t ft232r_write(struct ft232r_device_handle *dev, void *data, size_t count)
+ssize_t ft232r_write(struct ft232r_device_handle * const dev, const void * const data, const size_t count)
 {
 	uint16_t bufleft;
 	ssize_t r;
@@ -232,8 +351,10 @@ ssize_t ft232r_write(struct ft232r_device_handle *dev, void *data, size_t count)
 
 typedef ssize_t (*ft232r_rwfunc_t)(struct ft232r_device_handle *, void*, size_t);
 
-static ssize_t ft232r_rw_all(ft232r_rwfunc_t rwfunc, struct ft232r_device_handle *dev, void *data, size_t count)
+static
+ssize_t ft232r_rw_all(const void * const rwfunc_p, struct ft232r_device_handle * const dev, void * const data, size_t count)
 {
+	ft232r_rwfunc_t rwfunc = rwfunc_p;
 	char *p = data;
 	ssize_t writ = 0, total = 0;
 
@@ -245,9 +366,28 @@ static ssize_t ft232r_rw_all(ft232r_rwfunc_t rwfunc, struct ft232r_device_handle
 	return total ?: writ;
 }
 
-ssize_t ft232r_write_all(struct ft232r_device_handle *dev, void *data, size_t count)
+ssize_t ft232r_write_all(struct ft232r_device_handle * const dev, const void * const data_p, size_t count)
 {
-	return ft232r_rw_all(ft232r_write, dev, data, count);
+	const uint8_t *data = data_p;
+	if (dev->mpsse)
+	{
+		ssize_t e;
+		while (count > 0x10000)
+		{
+			e = ft232r_write_all(dev, data, 0x10000);
+			if (e != 0x10000)
+				return e;
+			data += 0x10000;
+			count -= 0x10000;
+		}
+		
+		const uint16_t ftdilen = count - 1;
+		const uint8_t cmd[] = { 0x11, ftdilen & 0xff, ftdilen >> 8 };
+		e = ft232r_rw_all(ft232r_write, dev, (void*)cmd, 3);
+		if (e != 3)
+			return e;
+	}
+	return ft232r_rw_all(ft232r_write, dev, (void*)data, count) + (data - (uint8_t*)data_p);
 }
 
 ssize_t ft232r_read(struct ft232r_device_handle *dev, void *data, size_t count)
@@ -260,14 +400,14 @@ ssize_t ft232r_read(struct ft232r_device_handle *dev, void *data, size_t count)
 	if (r < 0)
 		return r;
 	
-	// First 2 bytes of every 0x40 are FTDI status or something
+	// First 2 bytes of every packet are FTDI status or something
 	while (dev->ibufLen <= 2) {
 		// TODO: Implement a timeout for status byte repeating
 		int transferred = ft232r_readwrite(dev, dev->i, dev->ibuf, sizeof(dev->ibuf));
 		if (transferred <= 0)
 			return transferred;
 		dev->ibufLen = transferred;
-		for (adj = 0x40; dev->ibufLen > adj; adj += 0x40 - 2) {
+		for (adj = dev->iPktSz; dev->ibufLen > adj; adj += dev->iPktSz - 2) {
 			dev->ibufLen -= 2;
 			memmove(&dev->ibuf[adj], &dev->ibuf[adj+2], dev->ibufLen - adj);
 		}
@@ -319,6 +459,42 @@ bool ft232r_get_cbus_bits(struct ft232r_device_handle *dev, bool *out_sio0, bool
 	return true;
 }
 
+bool ft232h_mpsse_set_axbus(struct ft232r_device_handle * const ftdi, const uint8_t value, const uint8_t directions, const bool adbus)
+{
+	if (ft232r_flush(ftdi))
+		cgsleep_ms(1);
+	const uint8_t buf[] = { adbus ? FTDI_ADBUS_SET : FTDI_ACBUS_SET, value, directions };
+	return (ft232r_write(ftdi, buf, 3) == 3) && (ft232r_flush(ftdi) == 3);
+}
+
+ssize_t ft232h_mpsse_readwrite_all(struct ft232r_device_handle * const dev, void * const read_data_p, const void * const write_data_p, size_t count)
+{
+	uint8_t *read_data = read_data_p;
+	const uint8_t *write_data = write_data_p;
+	
+	while (count > 0x10000)
+	{
+		ft232h_mpsse_readwrite_all(dev, read_data, write_data, 0x10000);
+		read_data += 0x10000;
+		write_data += 0x10000;
+		count -= 0x10000;
+	}
+	
+	const uint16_t ftdilen = count - 1;
+	const uint8_t cmd[] = { 0x31, ftdilen & 0xff, ftdilen >> 8 };
+	ssize_t e;
+	
+	e = ft232r_rw_all(ft232r_write, dev, (void*)cmd, 3);
+	if (e != 3)
+		return e;
+	
+	e = ft232r_rw_all(ft232r_write, dev, (void*)write_data, count);
+	if (e != count)
+		return e;
+	
+	return ft232r_read_all(dev, read_data, count) + (read_data - (uint8_t*)read_data_p);
+}
+
 struct lowlevel_driver lowl_ft232r = {
 	.dname = "ft232r",
 	.devinfo_scan = ft232r_devinfo_scan,

+ 10 - 14
ft232r.h → lowl-ftdi.h

@@ -1,14 +1,5 @@
-/*
- * Copyright 2012 Luke Dashjr
- *
- * 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 3 of the License, or (at your option)
- * any later version.  See COPYING for more details.
- */
-
-#ifndef BFGMINER_FT232R_H
-#define BFGMINER_FT232R_H
+#ifndef BFG_LOWL_FTDI_H
+#define BFG_LOWL_FTDI_H
 
 #include <stdbool.h>
 #include <stdint.h>
@@ -25,17 +16,22 @@ enum ft232r_reset_purge {
 
 struct ft232r_device_handle;
 
-extern struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *);
+extern struct ft232r_device_handle *ft232r_open(const struct lowlevel_device_info *);
+extern struct ft232r_device_handle *ft232h_open_mpsse(const struct lowlevel_device_info *);
 extern void ft232r_close(struct ft232r_device_handle *);
 extern bool ft232r_purge_buffers(struct ft232r_device_handle *, enum ft232r_reset_purge);
 extern bool ft232r_set_bitmode(struct ft232r_device_handle *, uint8_t mask, uint8_t mode);
 extern ssize_t ft232r_flush(struct ft232r_device_handle *);
-extern ssize_t ft232r_write(struct ft232r_device_handle *, void *data, size_t count);
-extern ssize_t ft232r_write_all(struct ft232r_device_handle *, void *data, size_t count);
+extern ssize_t ft232r_write(struct ft232r_device_handle *, const void *data, size_t count);
+extern ssize_t ft232r_write_all(struct ft232r_device_handle *, const void *data, size_t count);
 extern ssize_t ft232r_read(struct ft232r_device_handle *, void *buf, size_t count);
 extern ssize_t ft232r_read_all(struct ft232r_device_handle *, void *data, size_t count);
 extern bool ft232r_get_pins(struct ft232r_device_handle *, uint8_t *pins);
 extern bool ft232r_set_cbus_bits(struct ft232r_device_handle *dev, bool sc, bool cs);
 extern bool ft232r_get_cbus_bits(struct ft232r_device_handle *dev, bool *out_sio0, bool *out_sio1);
+extern bool ft232h_mpsse_set_axbus(struct ft232r_device_handle *, uint8_t value, uint8_t directions, bool adbus);
+#define ft232h_mpsse_set_acbus(ftdi, val, dir)  ft232h_mpsse_set_axbus(ftdi, val, dir, false)
+#define ft232h_mpsse_set_adbus(ftdi, val, dir)  ft232h_mpsse_set_axbus(ftdi, val, dir, true)
+extern ssize_t ft232h_mpsse_readwrite_all(struct ft232r_device_handle *, void *read_data, const void *write_data, size_t count);
 
 #endif

+ 1 - 1
lowlevel.c

@@ -94,7 +94,7 @@ struct lowlevel_device_info *lowlevel_scan()
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
 #endif
 	
-#ifdef USE_X6500
+#ifdef NEED_BFG_LOWL_FTDI
 	devinfo_mid_list = lowl_ft232r.devinfo_scan();
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
 #endif

+ 1 - 1
lowlevel.h

@@ -57,7 +57,7 @@ extern struct lowlevel_device_info *lowlevel_ref(const struct lowlevel_device_in
 extern void lowlevel_devinfo_semicpy(struct lowlevel_device_info *dst, const struct lowlevel_device_info *src);
 extern void lowlevel_devinfo_free(struct lowlevel_device_info *);
 
-#ifdef USE_X6500
+#ifdef NEED_BFG_LOWL_FTDI
 extern struct lowlevel_driver lowl_ft232r;
 #endif
 #ifdef NEED_BFG_LOWL_HID