Browse Source

Merge pull request #519 from kanoi/master

Configure source for a new BaB driver
kanoi 12 years ago
parent
commit
92674e9ef6
8 changed files with 1912 additions and 4 deletions
  1. 10 1
      ASIC-README
  2. 4 0
      Makefile.am
  3. 1 0
      README
  4. 4 1
      api.c
  5. 3 0
      cgminer.c
  6. 19 2
      configure.ac
  7. 1870 0
      driver-bab.c
  8. 1 0
      miner.h

+ 10 - 1
ASIC-README

@@ -3,7 +3,8 @@ SUPPORTED DEVICES
 Currently supported devices include the Avalon (including BitBurner and
 Klondike), the Butterfly Labs SC range of devices, the ASICMINER block
 erupters, the BF1 (bitfury) USB (red and blue) devices, KnCminer Mercury,
-Saturn and Jupiter devices, and upcoming Hashfast devices.
+Saturn and Jupiter devices, BlackArrow Bitfury devices and upcoming
+Hashfast devices.
 
 No COM ports on windows or TTY devices will be used by cgminer as it
 communicates directly with them via USB so it is normal for them to not exist or
@@ -34,6 +35,14 @@ in the FPGA-README. Configuring them uses the same mechanism as outlined
 below for getting started with butterfly labs ASICs.
 
 
+BlackArrow Bitfury devices
+
+BlackArrow Bitfury devices need the --enable-bab option when compiling cgminer.
+
+The current BlackArrow Bitfury devices are similar to the Bitfury GPIO mining
+boards and come up as BaB. There are no options available for them.
+
+
 BITFURY devices
 
 Bitfury devices need the --enable-bitfury option when compiling cgminer.

+ 4 - 0
Makefile.am

@@ -100,6 +100,10 @@ if HAS_KLONDIKE
 cgminer_SOURCES += driver-klondike.c
 endif
 
+if HAS_BAB
+cgminer_SOURCES += driver-bab.c
+endif
+
 if HAS_MODMINER
 cgminer_SOURCES += driver-modminer.c
 bitstreamsdir = $(bindir)/bitstreams

+ 1 - 0
README

@@ -121,6 +121,7 @@ CGMiner specific configuration options:
   --enable-hashfast       Compile support for Hashfast (default disabled)
   --enable-icarus         Compile support for Icarus (default disabled)
   --enable-knc            Compile support for KnC miners (default disabled)
+  --enable-bab            Compile support for BlackArrow Bitfury (default disabled)
   --enable-klondike       Compile support for Klondike (default disabled)
   --enable-modminer       Compile support for ModMiner FPGAs(default disabled)
   --without-curses        Compile support for curses TUI (default enabled)

+ 4 - 1
api.c

@@ -29,7 +29,7 @@
 #include "miner.h"
 #include "util.h"
 
-#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_KLONDIKE) || defined(USE_KNC)
+#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_KLONDIKE) || defined(USE_KNC) || defined(USE_BAB)
 #define HAVE_AN_ASIC 1
 #endif
 
@@ -194,6 +194,9 @@ static const char *DEVICECODE = ""
 #ifdef USE_KNC
 			"KnC "
 #endif
+#ifdef USE_BAB
+			"BaB "
+#endif
 #ifdef USE_MODMINER
 			"MMQ "
 #endif

+ 3 - 0
cgminer.c

@@ -1628,6 +1628,9 @@ static char *opt_verusage_and_exit(const char *extra)
 #ifdef USE_KNC
 		"KnC "
 #endif
+#ifdef USE_BAB
+		"BaB "
+#endif
 #ifdef USE_MODMINER
 		"modminer "
 #endif

+ 19 - 2
configure.ac

@@ -287,6 +287,17 @@ if test "x$hashfast" = xyes; then
 fi
 AM_CONDITIONAL([HAS_HASHFAST], [test x$hashfast = xyes])
 
+bab="no"
+
+AC_ARG_ENABLE([bab],
+	[AC_HELP_STRING([--enable-bab],[Compile support for BlackArrow Bitfury (default disabled)])],
+	[bab=$enableval]
+	)
+if test "x$bab" = xyes; then
+	AC_DEFINE([USE_BAB], [1], [Defined to 1 if BlackArrow Bitfury support is wanted])
+fi
+AM_CONDITIONAL([HAS_BAB], [test x$bab = xyes])
+
 icarus="no"
 
 AC_ARG_ENABLE([icarus],
@@ -537,14 +548,14 @@ if test "x$opencl" != xno; then
 
 	else
 		echo "  OpenCL...............: NOT FOUND. GPU mining support DISABLED"
-		if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc" = xnonononononononono; then
+		if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc$bab" = xnononononononononono; then
 			AC_MSG_ERROR([No mining configured in])
 		fi
 		echo "  scrypt...............: Disabled (needs OpenCL)"
 	fi
 else
 	echo "  OpenCL...............: Detection overrided. GPU mining support DISABLED"
-	if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc" = xnonononononononono; then
+	if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc$bab" = xnononononononononono; then
 		AC_MSG_ERROR([No mining configured in])
 	fi
 	echo "  scrypt...............: Disabled (needs OpenCL)"
@@ -597,6 +608,12 @@ else
 	echo "  Hashfast.ASICs.......: Disabled"
 fi
 
+if test "x$bab" = xyes; then
+	echo "  BlackArrow.ASICs.....: Enabled"
+else
+	echo "  BlackArrow.ASICs.....: Disabled"
+fi
+
 if test "x$icarus" = xyes; then
 	echo "  Icarus.ASICs/FPGAs...: Enabled"
 else

+ 1870 - 0
driver-bab.c

@@ -0,0 +1,1870 @@
+/*
+ * Copyright 2013 Andrew Smith
+ * Copyright 2013 bitfury
+ *
+ * BitFury GPIO code based on chainminer code:
+ *	https://github.com/bfsb/chainminer
+ *
+ * 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 "compat.h"
+#include "miner.h"
+#include "sha2.h"
+
+/*
+ * Tested on RPi running Raspbian with BlackArrow BitFury V1 16 chip GPIO board
+ */
+
+#ifndef LINUX
+static void bab_detect(__maybe_unused bool hotplug)
+{
+}
+#else
+
+#include <unistd.h>
+#include <linux/spi/spidev.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#define BAB_SPI_BUS 0
+#define BAB_SPI_CHIP 0
+
+#define BAB_SPI_SPEED 96000
+#define BAB_SPI_BUFSIZ 1024
+
+#define BAB_ADDR(_n) (*((babinfo->gpio) + (_n)))
+
+#define BAB_INP_GPIO(_n) BAB_ADDR((_n) / 10) &= (~(7 << (((_n) % 10) * 3)))
+#define BAB_OUT_GPIO(_n) BAB_ADDR((_n) / 10) |= (1 << (((_n) % 10) * 3))
+#define BAB_OUT_GPIO_V(_n, _v) BAB_ADDR((_n) / 10) |= (((_v) <= 3 ? (_v) + 4 : \
+					((_v) == 4 ? 3 : 2)) << (((_n) % 10) * 3))
+
+#define BAB_GPIO_SET BAB_ADDR(7)
+#define BAB_GPIO_CLR BAB_ADDR(10)
+#define BAB_GPIO_LEVEL BAB_ADDR(13)
+
+#define BAB_MAXCHIPS 256
+#define BAB_MAXBUF (BAB_MAXCHIPS * 512)
+#define BAB_MAXBANKS 4
+#define BAB_CORES 16
+#define BAB_X_COORD 21
+#define BAB_Y_COORD 36
+
+#define BAB_BREAK ((uint8_t *)"\04")
+#define BAB_ASYNC ((uint8_t *)"\05")
+#define BAB_SYNC ((uint8_t *)"\06")
+
+#define BAB_FFL " - from %s %s() line %d"
+#define BAB_FFL_HERE __FILE__, __func__, __LINE__
+#define BAB_FFL_PASS file, func, line
+
+#define bab_reset(_bank, _times) _bab_reset(babcgpu, babinfo, _bank, _times)
+#define bab_txrx(_buf, _siz, _det) _bab_txrx(babcgpu, babinfo, _buf, _siz, _det, BAB_FFL_HERE)
+#define bab_add_buf(_data) _bab_add_buf(babcgpu, babinfo, _data, sizeof(_data)-1, BAB_FFL_HERE)
+#define BAB_ADD_BREAK() _bab_add_buf(babcgpu, babinfo, BAB_BREAK, 1, BAB_FFL_HERE)
+#define BAB_ADD_ASYNC() _bab_add_buf(babcgpu, babinfo, BAB_ASYNC, 1, BAB_FFL_HERE)
+#define bab_config_reg(_reg, _ena) _bab_config_reg(babcgpu, babinfo, _reg, _ena, BAB_FFL_HERE)
+#define bab_add_data(_addr, _data, _siz) _bab_add_data(babcgpu, babinfo, _addr, (const uint8_t *)(_data), _siz, BAB_FFL_HERE)
+
+#define BAB_ADD_MIN 4
+#define BAB_ADD_MAX 128
+
+#define BAB_STATE_DONE 0
+#define BAB_STATE_READY 1
+#define BAB_STATE_SENDING 2
+#define BAB_STATE_SENT 3
+#define BAB_STATE_READING 4
+
+#define BAB_SPI_BUFFERS 2
+
+#define BAB_BASEA 4
+#define BAB_BASEB 61
+#define BAB_COUNTERS 16
+static const uint8_t bab_counters[BAB_COUNTERS] = {
+	64,			64,
+	BAB_BASEA,		BAB_BASEA+4,
+	BAB_BASEA+2,		BAB_BASEA+2+16,
+	BAB_BASEA,		BAB_BASEA+1,
+	(BAB_BASEB)%65,		(BAB_BASEB+1)%65,
+	(BAB_BASEB+3)%65,	(BAB_BASEB+3+16)%65,
+	(BAB_BASEB+4)%65,	(BAB_BASEB+4+4)%65,
+	(BAB_BASEB+3+3)%65,	(BAB_BASEB+3+1+3)%65
+};
+
+#define BAB_W1 16
+static const uint32_t bab_w1[BAB_W1] = {
+	0,		0,	0,	0xffffffff,
+	0x80000000,	0,	0,	0,
+	0,		0,	0,	0,
+	0,		0,	0,	0x00000280
+};
+
+#define BAB_W2 8
+static const uint32_t bab_w2[BAB_W2] = {
+	0x80000000,	0,	0,	0,
+	0,		0,	0,	0x00000100
+};
+
+#define BAB_TEST_DATA 19
+static const uint32_t bab_test_data[BAB_TEST_DATA] = {
+	0xb0e72d8e,	0x1dc5b862,	0xe9e7c4a6,	0x3050f1f5,
+	0x8a1a6b7e,	0x7ec384e8,	0x42c1c3fc,	0x8ed158a1,
+	0x8a1a6b7e,	0x6f484872,	0x4ff0bb9b,	0x12c97f07,
+	0xb0e72d8e,	0x55d979bc,	0x39403296,	0x40f09e84,
+	0x8a0bb7b7,	0x33af304f,	0x0b290c1a //,	0xf0c4e61f
+};
+
+//maximum number of chips on alternative bank
+// #define BANKCHIPS 64
+
+/*
+ * maximum chip speed available for auto tuner
+ * speed/nrate/hrate/watt
+ *    53/   97/  100/  84
+ *    54/   98/  107/  88
+ *    55/   99/  115/  93
+ *    56/  101/  125/  99
+ */
+#define BAB_MAXSPEED 57
+#define BAB_DEFSPEED 54
+#define BAB_MINSPEED 52
+
+#define MIDSTATE_BYTES 32
+#define MERKLE_OFFSET 64
+#define MERKLE_BYTES 12
+#define BLOCK_HEADER_BYTES 80
+
+#define MIDSTATE_UINTS (MIDSTATE_BYTES / sizeof(uint32_t))
+#define DATA_UINTS ((BLOCK_HEADER_BYTES / sizeof(uint32_t)) - 1)
+
+// Auto adjust
+#define BAB_AUTO_REG 0
+#define BAB_AUTO_VAL 0x01
+// iclk
+#define BAB_ICLK_REG 1
+#define BAB_ICLK_VAL 0x02
+// No fast clock
+#define BAB_FAST_REG 2
+#define BAB_FAST_VAL 0x04
+// Divide by 2
+#define BAB_DIV2_REG 3
+#define BAB_DIV2_VAL 0x08
+// Slow Clock
+#define BAB_SLOW_REG 4
+#define BAB_SLOW_VAL 0x10
+// No oclk
+#define BAB_OCLK_REG 6
+#define BAB_OCLK_VAL 0x20
+// Has configured
+#define BAB_CFGD_VAL 0x40
+
+#define BAB_DEFCONF (BAB_AUTO_VAL | \
+		     BAB_ICLK_VAL | \
+		     BAB_DIV2_VAL | \
+		     BAB_SLOW_VAL)
+
+#define BAB_REG_CLR_FROM 7
+#define BAB_REG_CLR_TO 11
+
+#define BAB_AUTO_SET(_c) ((_c) & BAB_AUTO_VAL)
+#define BAB_ICLK_SET(_c) ((_c) & BAB_ICLK_VAL)
+#define BAB_FAST_SET(_c) ((_c) & BAB_FAST_VAL)
+#define BAB_DIV2_SET(_c) ((_c) & BAB_DIV2_VAL)
+#define BAB_SLOW_SET(_c) ((_c) & BAB_SLOW_VAL)
+#define BAB_OCLK_SET(_c) ((_c) & BAB_OCLK_VAL)
+#define BAB_CFGD_SET(_c) ((_c) & BAB_CFGD_VAL)
+
+#define BAB_AUTO_BIT(_c) (BAB_AUTO_SET(_c) ? true : false)
+#define BAB_ICLK_BIT(_c) (BAB_ICLK_SET(_c) ? false : true)
+#define BAB_FAST_BIT(_c) (BAB_FAST_SET(_c) ? true : false)
+#define BAB_DIV2_BIT(_c) (BAB_DIV2_SET(_c) ? false : true)
+#define BAB_SLOW_BIT(_c) (BAB_SLOW_SET(_c) ? true : false)
+#define BAB_OCLK_BIT(_c) (BAB_OCLK_SET(_c) ? true : false)
+
+#define BAB_COUNT_ADDR 0x0100
+#define BAB_W1A_ADDR 0x1000
+#define BAB_W1B_ADDR 0x1400
+#define BAB_W2_ADDR 0x1900
+#define BAB_INP_ADDR 0x3000
+#define BAB_OSC_ADDR 0x6000
+#define BAB_REG_ADDR 0x7000
+
+/*
+ * valid: 0x01 0x03 0x07 0x0F 0x1F 0x3F 0x7F 0xFF
+ * max { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00 }
+ * max { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00 }
+ * avg { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00 }
+ * slo { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00 }
+ * min { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+ * good: 0x1F (97) 0x3F (104) 0x7F (109) 0xFF (104)
+ */
+
+#define BAB_OSC 8
+static const uint8_t bab_osc_bits[BAB_OSC] =
+	{ 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
+
+static const uint8_t bab_reg_ena[4] = { 0xc1, 0x6a, 0x59, 0xe3 };
+static const uint8_t bab_reg_dis[4] = { 0x00, 0x00, 0x00, 0x00 };
+
+#define BAB_NONCE_OFFSETS 3
+static const uint32_t bab_nonce_offsets[] = {-0x800000, 0, -0x400000};
+
+struct bab_work_send {
+	uint32_t midstate[MIDSTATE_UINTS];
+	uint32_t ms3steps[MIDSTATE_UINTS];
+	uint32_t merkle7;
+	uint32_t ntime;
+	uint32_t bits;
+};
+
+#define BAB_REPLY_NONCES 16
+struct bab_work_reply {
+	uint32_t nonce[BAB_REPLY_NONCES];
+	uint32_t jobsel;
+};
+
+#define MAX_BLISTS 4096
+
+typedef struct blist {
+	struct blist *prev;
+	struct blist *next;
+	struct work *work;
+	int nonces;
+} BLIST;
+
+#define MAX_RLISTS 256
+
+typedef struct rlist {
+	struct rlist *prev;
+	struct rlist *next;
+	int chip;
+	uint32_t nonce;
+	bool first_second;
+} RLIST;
+
+struct bab_info {
+	struct thr_info spi_thr;
+	struct thr_info res_thr;
+
+	pthread_mutex_t spi_lock;
+	pthread_mutex_t res_lock;
+	pthread_mutex_t did_lock;
+	cglock_t blist_lock;
+
+	// All GPIO goes through this
+	volatile unsigned *gpio;
+
+	int spifd;
+	int chips;
+	uint32_t chip_spis[BAB_MAXCHIPS+1];
+
+	int buffer;
+	int buf_status[BAB_SPI_BUFFERS];
+	uint8_t buf_write[BAB_SPI_BUFFERS][BAB_MAXBUF];
+	uint8_t buf_read[BAB_SPI_BUFFERS][BAB_MAXBUF];
+	uint32_t buf_used[BAB_SPI_BUFFERS];
+	uint32_t chip_off[BAB_SPI_BUFFERS][BAB_MAXCHIPS+1];
+	uint32_t bank_off[BAB_SPI_BUFFERS][BAB_MAXBANKS+2];
+
+	struct bab_work_send chip_input[BAB_MAXCHIPS];
+	struct bab_work_reply chip_results[BAB_MAXCHIPS];
+	struct bab_work_reply chip_prev[BAB_MAXCHIPS];
+
+	uint8_t chip_fast[BAB_MAXCHIPS];
+	uint8_t chip_conf[BAB_MAXCHIPS];
+	uint8_t old_fast[BAB_MAXCHIPS];
+	uint8_t old_conf[BAB_MAXCHIPS];
+	uint8_t chip_bank[BAB_MAXCHIPS+1];
+
+	uint8_t osc[BAB_OSC];
+
+	int fixchip;
+
+	/*
+	 * Ignore errors in the first work reply since
+	 * they may be from a previous run or random junk
+	 * There can be >100 with just a 16 chip board
+	 */
+	uint32_t initial_ignored;
+	bool nonce_before[BAB_MAXCHIPS];
+	bool not_first_reply[BAB_MAXCHIPS];
+
+	// Stats
+	struct timeval chip_start[BAB_MAXCHIPS];
+	int chip_busy[BAB_MAXCHIPS];
+	uint64_t core_good[BAB_MAXCHIPS][BAB_CORES];
+	uint64_t core_bad[BAB_MAXCHIPS][BAB_CORES];
+	uint64_t chip_spie[BAB_MAXCHIPS]; // spi errors
+	uint64_t chip_miso[BAB_MAXCHIPS]; // msio errors
+	uint64_t chip_nonces[BAB_MAXCHIPS];
+	uint64_t chip_good[BAB_MAXCHIPS];
+	uint64_t chip_bad[BAB_MAXCHIPS];
+	uint64_t chip_ncore[BAB_MAXCHIPS][BAB_X_COORD][BAB_Y_COORD];
+
+	uint64_t untested_nonces;
+	uint64_t tested_nonces;
+
+	uint64_t new_nonces;
+	uint64_t ok_nonces;
+
+	uint64_t nonce_offset_count[BAB_NONCE_OFFSETS];
+	uint64_t total_tests;
+	uint64_t max_tests_per_nonce;
+	uint64_t total_links;
+	uint64_t max_links;
+
+	int blist_count;
+	int bfree_count;
+	int work_count;
+	int chip_count;
+	BLIST *bfree_list;
+	BLIST *work_list;
+	BLIST *chip_list[BAB_MAXCHIPS];
+
+	int rlist_count;
+	int rfree_count;
+	int res_count;
+	RLIST *rfree_list;
+	RLIST *res_list_head;
+	RLIST *res_list_tail;
+
+	struct timeval last_did;
+
+	bool initialised;
+};
+
+static BLIST *new_blist_set(struct cgpu_info *babcgpu)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	BLIST *blist = NULL;
+	int i;
+
+	blist = calloc(MAX_BLISTS, sizeof(*blist));
+	if (!blist)
+		quithere(1, "Failed to calloc blist - when old count=%d", babinfo->blist_count);
+
+	babinfo->blist_count += MAX_BLISTS;
+	babinfo->bfree_count = MAX_BLISTS;
+
+	blist[0].prev = NULL;
+	blist[0].next = &(blist[1]);
+	for (i = 1; i < MAX_BLISTS-1; i++) {
+		blist[i].prev = &blist[i-1];
+		blist[i].next = &blist[i+1];
+	}
+	blist[MAX_BLISTS-1].prev = &(blist[MAX_BLISTS-2]);
+	blist[MAX_BLISTS-1].next = NULL;
+
+	return blist;
+}
+
+static BLIST *next_work(struct cgpu_info *babcgpu, int chip)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	BLIST *bitem;
+
+	cg_wlock(&babinfo->blist_lock);
+	bitem = babinfo->work_list;
+	if (bitem) {
+		// Unlink it from work
+		if (bitem->next)
+			bitem->next->prev = NULL;
+		babinfo->work_list = bitem->next;
+		babinfo->work_count--;
+
+		// Add it to the chip
+		bitem->next = babinfo->chip_list[chip];
+		bitem->prev = NULL;
+		if (bitem->next)
+			bitem->next->prev = bitem;
+		babinfo->chip_list[chip] = bitem;
+		babinfo->chip_count++;
+	}
+	cg_wunlock(&babinfo->blist_lock);
+
+	return bitem;
+}
+
+static void discard_last(struct cgpu_info *babcgpu, int chip)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	BLIST *bitem;
+
+	cg_wlock(&babinfo->blist_lock);
+	bitem = babinfo->chip_list[chip];
+	if (bitem) {
+		// Unlink it from the chip
+		if (bitem->next)
+			bitem->next->prev = NULL;
+		babinfo->chip_list[chip] = bitem->next;
+		babinfo->chip_count--;
+
+		// Put it in the free list
+		bitem->next = babinfo->bfree_list;
+		bitem->prev = NULL;
+		if (bitem->next)
+			bitem->next->prev = bitem;
+		babinfo->bfree_list = bitem;
+		babinfo->bfree_count++;
+	}
+	cg_wunlock(&babinfo->blist_lock);
+}
+
+static BLIST *store_work(struct cgpu_info *babcgpu, struct work *work)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	BLIST *bitem = NULL;
+	int ran_out = 0;
+
+	cg_wlock(&babinfo->blist_lock);
+
+	if (babinfo->bfree_list == NULL) {
+		ran_out = babinfo->blist_count;
+		babinfo->bfree_list = new_blist_set(babcgpu);
+	}
+
+	// unlink from free
+	bitem = babinfo->bfree_list;
+	babinfo->bfree_list = babinfo->bfree_list->next;
+	if (babinfo->bfree_list)
+		babinfo->bfree_list->prev = NULL;
+	babinfo->bfree_count--;
+
+	// add to work
+	bitem->next = babinfo->work_list;
+	bitem->prev = NULL;
+	if (bitem->next)
+		bitem->next->prev = bitem;
+	babinfo->work_list = bitem;
+	babinfo->work_count++;
+
+	bitem->work = work;
+	bitem->nonces = 0;
+
+	cg_wunlock(&babinfo->blist_lock);
+
+	if (ran_out > 0) {
+		applog(LOG_ERR, "%s%i: BLIST used count exceeded %d, now %d (work=%d chip=%d)",
+				babcgpu->drv->name, babcgpu->device_id,
+				ran_out, babinfo->blist_count,
+				babinfo->work_count,
+				babinfo->chip_count);
+	}
+
+	return bitem;
+}
+
+static void free_blist(struct cgpu_info *babcgpu, BLIST *bhead, int chip)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	struct work *work;
+	BLIST *bitem;
+
+	if (!bhead)
+		return;
+
+	// Unlink it from the chip
+	cg_wlock(&babinfo->blist_lock);
+	if (unlikely(bhead == babinfo->chip_list[chip])) {
+		// Removing the chip head is an error
+		bhead = bhead->next;
+		babinfo->chip_list[chip]->next = NULL;
+	} else
+		bhead->prev->next = NULL;
+	bitem = bhead;
+	while (bitem) {
+		babinfo->chip_count--;
+		bitem = bitem->next;
+	}
+	cg_wunlock(&babinfo->blist_lock);
+
+	while (bhead) {
+		bitem = bhead;
+		bhead = bitem->next;
+
+		// add to free
+		cg_wlock(&babinfo->blist_lock);
+		bitem->next = babinfo->bfree_list;
+		if (babinfo->bfree_list)
+			babinfo->bfree_list->prev = bitem;
+		bitem->prev = NULL;
+		babinfo->bfree_list = bitem;
+		babinfo->bfree_count++;
+		work = bitem->work;
+		cg_wunlock(&babinfo->blist_lock);
+
+		work_completed(babcgpu, work);
+	}
+
+}
+
+static RLIST *new_rlist_set(struct cgpu_info *babcgpu)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	RLIST *rlist = NULL;
+	int i;
+
+	rlist = calloc(MAX_RLISTS, sizeof(*rlist));
+	if (!rlist)
+		quithere(1, "Failed to calloc rlist - when old count=%d", babinfo->rlist_count);
+
+	babinfo->rlist_count += MAX_RLISTS;
+	babinfo->rfree_count = MAX_RLISTS;
+
+	rlist[0].prev = NULL;
+	rlist[0].next = &(rlist[1]);
+	for (i = 1; i < MAX_RLISTS-1; i++) {
+		rlist[i].prev = &rlist[i-1];
+		rlist[i].next = &rlist[i+1];
+	}
+	rlist[MAX_RLISTS-1].prev = &(rlist[MAX_RLISTS-2]);
+	rlist[MAX_RLISTS-1].next = NULL;
+
+	return rlist;
+}
+
+static RLIST *store_nonce(struct cgpu_info *babcgpu, int chip, uint32_t nonce, bool first_second)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	RLIST *ritem = NULL;
+	int ran_out = 0;
+
+	mutex_lock(&(babinfo->res_lock));
+
+	if (babinfo->rfree_list == NULL) {
+		ran_out = babinfo->rlist_count;
+		babinfo->rfree_list = new_rlist_set(babcgpu);
+	}
+
+	// unlink from rfree
+	ritem = babinfo->rfree_list;
+	babinfo->rfree_list = babinfo->rfree_list->next;
+	if (babinfo->rfree_list)
+		babinfo->rfree_list->prev = NULL;
+	babinfo->rfree_count--;
+
+	// add to head of results
+	ritem->next = babinfo->res_list_head;
+	ritem->prev = NULL;
+	babinfo->res_list_head = ritem;
+	if (ritem->next)
+		ritem->next->prev = ritem;
+	else
+		babinfo->res_list_tail = ritem;
+
+	babinfo->res_count++;
+
+	ritem->chip = chip;
+	ritem->nonce = nonce;
+	ritem->first_second = first_second;
+
+	mutex_unlock(&(babinfo->res_lock));
+
+	if (ran_out > 0) {
+		applog(LOG_ERR, "%s%i: RLIST used count exceeded %d, now %d (work=%d chip=%d)",
+				babcgpu->drv->name, babcgpu->device_id,
+				ran_out, babinfo->rlist_count,
+				babinfo->work_count,
+				babinfo->chip_count);
+	}
+
+	return ritem;
+}
+
+static bool oldest_nonce(struct cgpu_info *babcgpu, int *chip, uint32_t *nonce, bool *first_second)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	RLIST *ritem = NULL;
+	bool found = false;
+
+	mutex_lock(&(babinfo->res_lock));
+
+	if (babinfo->res_list_tail) {
+		// unlink from res
+		ritem = babinfo->res_list_tail;
+		if (ritem->prev) {
+			ritem->prev->next = NULL;
+			babinfo->res_list_tail = ritem->prev;
+		} else
+			babinfo->res_list_head = babinfo->res_list_tail = NULL;
+
+		babinfo->res_count--;
+
+		found = true;
+		*chip = ritem->chip;
+		*nonce = ritem->nonce;
+		*first_second = ritem->first_second;
+
+		// add to rfree
+		ritem->next = babinfo->rfree_list;
+		ritem->prev = NULL;
+		if (ritem->next)
+			ritem->next->prev = ritem;
+		babinfo->rfree_list = ritem;
+
+		babinfo->rfree_count++;
+	}
+
+	mutex_unlock(&(babinfo->res_lock));
+
+	return found;
+}
+
+static void _bab_reset(__maybe_unused struct cgpu_info *babcgpu, struct bab_info *babinfo, int bank, int times)
+{
+	const int banks[4] = { 18, 23, 24, 25 };
+	int i;
+
+	BAB_INP_GPIO(10);
+	BAB_OUT_GPIO(10);
+	BAB_INP_GPIO(11);
+	BAB_OUT_GPIO(11);
+
+	if (bank) {
+		for (i = 0; i < 4; i++) {
+			BAB_INP_GPIO(banks[i]);
+			BAB_OUT_GPIO(banks[i]);
+			if (bank == i+1)
+				BAB_GPIO_SET = 1 << banks[i];
+			else
+				BAB_GPIO_CLR = 1 << banks[i];
+		}
+		cgsleep_us(4096);
+	} else {
+		for (i = 0; i < 4; i++)
+			BAB_INP_GPIO(banks[i]);
+	}
+
+	BAB_GPIO_SET = 1 << 11;
+	for (i = 0; i < times; i++) { // 1us = 1MHz
+		BAB_GPIO_SET = 1 << 10;
+		cgsleep_us(1);
+		BAB_GPIO_CLR = 1 << 10;
+		cgsleep_us(1);
+	}
+	BAB_GPIO_CLR = 1 << 11;
+	BAB_INP_GPIO(11);
+	BAB_INP_GPIO(10);
+	BAB_INP_GPIO(9);
+	BAB_OUT_GPIO_V(11, 0);
+	BAB_OUT_GPIO_V(10, 0);
+	BAB_OUT_GPIO_V(9, 0);
+}
+
+// TODO: handle a false return where this is called?
+static bool _bab_txrx(struct cgpu_info *babcgpu, struct bab_info *babinfo, int buf, uint32_t siz, bool detect_ignore, const char *file, const char *func, const int line)
+{
+	int bank, i;
+	uint32_t pos;
+	struct spi_ioc_transfer tran;
+	uintptr_t rbuf, wbuf;
+
+	wbuf = (uintptr_t)(babinfo->buf_write[buf]);
+	rbuf = (uintptr_t)(babinfo->buf_read[buf]);
+
+	memset(&tran, 0, sizeof(tran));
+	tran.delay_usecs = 0;
+	tran.speed_hz = BAB_SPI_SPEED;
+
+	i = 0;
+	pos = 0;
+	for (bank = 0; bank <= BAB_MAXBANKS; bank++) {
+		if (babinfo->bank_off[buf][bank]) {
+			bab_reset(bank, 64);
+			break;
+		}
+	}
+
+	if (unlikely(bank > BAB_MAXBANKS)) {
+		applog(LOG_ERR, "%s%d: %s() failed to find a bank" BAB_FFL,
+				babcgpu->drv->name, babcgpu->device_id,
+				__func__, BAB_FFL_PASS);
+		return false;
+	}
+
+	while (siz > 0) {
+		tran.tx_buf = wbuf;
+		tran.rx_buf = rbuf;
+		tran.speed_hz = BAB_SPI_SPEED;
+		if (pos == babinfo->bank_off[buf][bank]) {
+			for (; ++bank <= BAB_MAXBANKS; ) {
+				if (babinfo->bank_off[buf][bank] > pos) {
+					bab_reset(bank, 64);
+					break;
+				}
+			}
+		}
+		if (siz < BAB_SPI_BUFSIZ)
+			tran.len = siz;
+		else
+			tran.len = BAB_SPI_BUFSIZ;
+
+		if (pos < babinfo->bank_off[buf][bank] &&
+		    babinfo->bank_off[buf][bank] < (pos + tran.len))
+			tran.len = babinfo->bank_off[buf][bank] - pos;
+
+		for (; i < babinfo->chips; i++) {
+			if (!babinfo->chip_off[buf][i])
+				continue;
+			if (babinfo->chip_off[buf][i] >= pos + tran.len) {
+				tran.speed_hz = babinfo->chip_spis[i];
+				break;
+			}
+		}
+
+		if (unlikely(i > babinfo->chips)) {
+			applog(LOG_ERR, "%s%d: %s() failed to find chip" BAB_FFL,
+					babcgpu->drv->name, babcgpu->device_id,
+					__func__, BAB_FFL_PASS);
+			return false;
+		}
+
+		if (unlikely(babinfo->chip_spis[i] == BAB_SPI_SPEED)) {
+			applog(LOG_DEBUG, "%s%d: %s() chip[%d] speed %d shouldn't be %d" BAB_FFL,
+						babcgpu->drv->name, babcgpu->device_id,
+						__func__, i, (int)babinfo->chip_spis[i],
+						BAB_SPI_SPEED, BAB_FFL_PASS);
+		}
+
+		if (unlikely(tran.speed_hz == BAB_SPI_SPEED)) {
+			applog(LOG_DEBUG, "%s%d: %s() transfer speed %d shouldn't be %d" BAB_FFL,
+						babcgpu->drv->name, babcgpu->device_id,
+						__func__, (int)tran.speed_hz,
+						BAB_SPI_SPEED, BAB_FFL_PASS);
+		}
+
+		if (ioctl(babinfo->spifd, SPI_IOC_MESSAGE(1), (void *)&tran) < 0) {
+			if (!detect_ignore || errno != 110) {
+				applog(LOG_ERR, "%s%d: ioctl failed err=%d" BAB_FFL,
+						babcgpu->drv->name, babcgpu->device_id,
+						errno, BAB_FFL_PASS);
+			}
+			return false;
+		}
+
+		siz -= tran.len;
+		wbuf += tran.len;
+		rbuf += tran.len;
+		pos += tran.len;
+	}
+	mutex_lock(&(babinfo->did_lock));
+	cgtime(&(babinfo->last_did));
+	mutex_unlock(&(babinfo->did_lock));
+	return true;
+}
+
+static void _bab_add_buf_rev(__maybe_unused struct cgpu_info *babcgpu, struct bab_info *babinfo, const uint8_t *data, uint32_t siz, const char *file, const char *func, const int line)
+{
+	uint8_t tmp;
+	uint32_t now_used, i;
+	int buf;
+
+	buf = babinfo->buffer;
+	now_used = babinfo->buf_used[buf];
+	if (now_used + siz >= BAB_MAXBUF) {
+		quitfrom(1, file, func, line,
+			"%s() buffer %d limit of %d exceeded=%d siz=%d",
+			__func__, buf, BAB_MAXBUF, now_used + siz, siz);
+	}
+
+	for (i = 0; i < siz; i++) {
+		tmp = data[i];
+		tmp = ((tmp & 0xaa)>>1) | ((tmp & 0x55) << 1);
+		tmp = ((tmp & 0xcc)>>2) | ((tmp & 0x33) << 2);
+		tmp = ((tmp & 0xf0)>>4) | ((tmp & 0x0f) << 4);
+		babinfo->buf_write[buf][now_used + i] = tmp;
+	}
+
+	babinfo->buf_used[buf] += siz;
+}
+
+static void _bab_add_buf(__maybe_unused struct cgpu_info *babcgpu, struct bab_info *babinfo, const uint8_t *data, size_t siz, const char *file, const char *func, const int line)
+{
+	uint32_t now_used;
+	int buf;
+
+	buf = babinfo->buffer;
+	now_used = babinfo->buf_used[buf];
+	if (now_used + siz >= BAB_MAXBUF) {
+		quitfrom(1, file, func, line,
+			"%s() buffer %d limit of %d exceeded=%d siz=%d",
+			__func__, buf, BAB_MAXBUF, (int)(now_used + siz), (int)siz);
+	}
+
+	memcpy(&(babinfo->buf_write[buf][now_used]), data, siz);
+	babinfo->buf_used[buf] += siz;
+}
+
+static void _bab_add_data(struct cgpu_info *babcgpu, struct bab_info *babinfo, uint32_t addr, const uint8_t *data, size_t siz, const char *file, const char *func, const int line)
+{
+	uint8_t tmp[3];
+	int trf_siz;
+
+	if (siz < BAB_ADD_MIN || siz > BAB_ADD_MAX) {
+		quitfrom(1, file, func, line,
+			"%s() called with invalid siz=%d (min=%d max=%d)",
+			__func__, (int)siz, BAB_ADD_MIN, BAB_ADD_MAX);
+	}
+	trf_siz = siz / 4;
+	tmp[0] = (trf_siz - 1) | 0xE0;
+	tmp[1] = (addr >> 8) & 0xff;
+	tmp[2] = addr & 0xff;
+	_bab_add_buf(babcgpu, babinfo, tmp, sizeof(tmp), BAB_FFL_PASS);
+	_bab_add_buf_rev(babcgpu, babinfo, data, siz, BAB_FFL_PASS);
+}
+
+static void _bab_config_reg(struct cgpu_info *babcgpu, struct bab_info *babinfo, uint32_t reg, bool enable, const char *file, const char *func, const int line)
+{
+	if (enable) {
+		_bab_add_data(babcgpu, babinfo, BAB_REG_ADDR + reg*32,
+				bab_reg_ena, sizeof(bab_reg_ena), BAB_FFL_PASS);
+	} else {
+		_bab_add_data(babcgpu, babinfo, BAB_REG_ADDR + reg*32,
+				bab_reg_dis, sizeof(bab_reg_dis), BAB_FFL_PASS);
+	}
+
+}
+
+static void bab_set_osc(struct bab_info *babinfo, int chip)
+{
+	int fast, i;
+
+	fast = babinfo->chip_fast[chip];
+
+	for (i = 0; i < BAB_OSC && fast > BAB_OSC; i++, fast -= BAB_OSC) {
+		babinfo->osc[i] = 0xff;
+	}
+	if (i < BAB_OSC && fast > 0 && fast <= BAB_OSC)
+		babinfo->osc[i++] = bab_osc_bits[fast - 1];
+	for (; i < BAB_OSC; i++)
+		babinfo->osc[i] = 0x00;
+
+	applog(LOG_DEBUG, "@osc(chip=%d) fast=%d 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", chip, fast, babinfo->osc[0], babinfo->osc[1], babinfo->osc[2], babinfo->osc[3], babinfo->osc[4], babinfo->osc[5], babinfo->osc[6], babinfo->osc[7]);
+}
+
+static bool bab_put(struct cgpu_info *babcgpu, struct bab_info *babinfo)
+{
+	int buf, i, reg, bank = 0;
+
+	babinfo->buffer = -1;
+
+	mutex_lock(&(babinfo->spi_lock));
+	if (babinfo->buf_status[0] == BAB_STATE_DONE) {
+		babinfo->buffer = 0;
+	} else if (babinfo->buf_status[1] == BAB_STATE_DONE) {
+		babinfo->buffer = 1;
+	} else if (babinfo->buf_status[0] == BAB_STATE_READY) {
+		babinfo->buf_status[0] = BAB_STATE_DONE;
+		babinfo->buffer = 0;
+	} else if (babinfo->buf_status[1] == BAB_STATE_READY) {
+		babinfo->buf_status[1] = BAB_STATE_DONE;
+		babinfo->buffer = 1;
+	}
+	mutex_unlock(&(babinfo->spi_lock));
+
+	if (babinfo->buffer == -1)
+		return false;
+
+	buf = babinfo->buffer;
+	babinfo->buf_used[buf] = 0;
+	memset(babinfo->bank_off[buf], 0, sizeof(babinfo->bank_off) / BAB_SPI_BUFFERS);
+
+	BAB_ADD_BREAK();
+	for (i = 0; i < babinfo->chips; i++) {
+		if (babinfo->chip_bank[i] != bank) {
+			babinfo->bank_off[buf][bank] = babinfo->buf_used[buf];
+			bank = babinfo->chip_bank[i];
+			BAB_ADD_BREAK();
+		}
+		if (i == babinfo->fixchip &&
+		    (BAB_CFGD_SET(babinfo->chip_conf[i]) ||
+		     !babinfo->chip_conf[i])) {
+			bab_set_osc(babinfo, i);
+			bab_add_data(BAB_OSC_ADDR, babinfo->osc, sizeof(babinfo->osc));
+			bab_config_reg(BAB_ICLK_REG, BAB_ICLK_BIT(babinfo->chip_conf[i]));
+			bab_config_reg(BAB_FAST_REG, BAB_FAST_BIT(babinfo->chip_conf[i]));
+			bab_config_reg(BAB_DIV2_REG, BAB_DIV2_BIT(babinfo->chip_conf[i]));
+			bab_config_reg(BAB_SLOW_REG, BAB_SLOW_BIT(babinfo->chip_conf[i]));
+			bab_config_reg(BAB_OCLK_REG, BAB_OCLK_BIT(babinfo->chip_conf[i]));
+			for (reg = BAB_REG_CLR_FROM; reg <= BAB_REG_CLR_TO; reg++)
+				bab_config_reg(reg, false);
+			if (babinfo->chip_conf[i]) {
+				bab_add_data(BAB_COUNT_ADDR, bab_counters, sizeof(bab_counters));
+				bab_add_data(BAB_W1A_ADDR, bab_w1, sizeof(bab_w1));
+				bab_add_data(BAB_W1B_ADDR, bab_w1, sizeof(bab_w1)/2);
+				bab_add_data(BAB_W2_ADDR, bab_w2, sizeof(bab_w2));
+				babinfo->chip_conf[i] ^= BAB_CFGD_VAL;
+			}
+			babinfo->old_fast[i] = babinfo->chip_fast[i];
+			babinfo->old_conf[i] = babinfo->chip_conf[i];
+		} else {
+			if (babinfo->old_fast[i] != babinfo->chip_fast[i]) {
+				bab_set_osc(babinfo, i);
+				bab_add_data(BAB_OSC_ADDR, babinfo->osc, sizeof(babinfo->osc));
+				babinfo->old_fast[i] = babinfo->chip_fast[i];
+			}
+			if (babinfo->old_conf[i] != babinfo->chip_conf[i]) {
+				if (BAB_ICLK_SET(babinfo->old_conf[i]) !=
+						 BAB_ICLK_SET(babinfo->chip_conf[i]))
+					bab_config_reg(BAB_ICLK_REG,
+							BAB_ICLK_BIT(babinfo->chip_conf[i]));
+				if (BAB_FAST_SET(babinfo->old_conf[i]) !=
+						 BAB_FAST_SET(babinfo->chip_conf[i]))
+					bab_config_reg(BAB_FAST_REG,
+							BAB_FAST_BIT(babinfo->chip_conf[i]));
+				if (BAB_DIV2_SET(babinfo->old_conf[i]) !=
+						 BAB_DIV2_SET(babinfo->chip_conf[i]))
+					bab_config_reg(BAB_DIV2_REG,
+							BAB_DIV2_BIT(babinfo->chip_conf[i]));
+				if (BAB_SLOW_SET(babinfo->old_conf[i]) !=
+						 BAB_SLOW_SET(babinfo->chip_conf[i]))
+					bab_config_reg(BAB_SLOW_REG,
+							BAB_SLOW_BIT(babinfo->chip_conf[i]));
+				if (BAB_OCLK_SET(babinfo->old_conf[i]) !=
+						 BAB_OCLK_SET(babinfo->chip_conf[i]))
+					bab_config_reg(BAB_OCLK_REG,
+							BAB_OCLK_BIT(babinfo->chip_conf[i]));
+				babinfo->old_conf[i] = babinfo->chip_conf[i];
+			}
+		}
+		babinfo->chip_off[buf][i] = babinfo->buf_used[buf] + 3;
+		if (babinfo->chip_conf[i])
+			bab_add_data(BAB_INP_ADDR, (uint8_t *)(&(babinfo->chip_input[i])),
+					sizeof(babinfo->chip_input[i]));
+
+		BAB_ADD_ASYNC();
+	}
+	babinfo->chip_off[buf][i] = babinfo->buf_used[buf];
+	babinfo->bank_off[buf][bank] = babinfo->buf_used[buf];
+
+	mutex_lock(&(babinfo->spi_lock));
+	babinfo->buf_status[buf] = BAB_STATE_READY;
+	mutex_unlock(&(babinfo->spi_lock));
+
+	babinfo->fixchip = (babinfo->fixchip + 1) % babinfo->chips;
+	return true;
+}
+
+static bool bab_get(__maybe_unused struct cgpu_info *babcgpu, struct bab_info *babinfo)
+{
+	int buf, i;
+
+	babinfo->buffer = -1;
+
+	mutex_lock(&(babinfo->spi_lock));
+	if (babinfo->buf_status[0] == BAB_STATE_SENT) {
+		babinfo->buf_status[0] = BAB_STATE_READING;
+		babinfo->buffer = 0;
+	} else if (babinfo->buf_status[1] == BAB_STATE_SENT) {
+			babinfo->buf_status[1] = BAB_STATE_READING;
+			babinfo->buffer = 1;
+	}
+	mutex_unlock(&(babinfo->spi_lock));
+
+	if (babinfo->buffer == -1)
+		return false;
+
+	buf = babinfo->buffer;
+	for (i = 0; i < babinfo->chips; i++) {
+		if (babinfo->chip_conf[i] & 0x7f) {
+			memcpy((void *)&(babinfo->chip_results[i]),
+				(void *)(babinfo->buf_read[buf] + babinfo->chip_off[buf][i]),
+				sizeof(babinfo->chip_results[0]));
+		}
+	}
+
+	mutex_lock(&(babinfo->spi_lock));
+	babinfo->buf_status[buf] = BAB_STATE_DONE;
+	mutex_unlock(&(babinfo->spi_lock));
+
+	return true;
+}
+
+void bab_detect_chips(struct cgpu_info *babcgpu, struct bab_info *babinfo, int bank, int first, int last)
+{
+	int buf, i, reg, j;
+
+	if (sizeof(struct bab_work_send) != sizeof(bab_test_data)) {
+		quithere(1, "struct bab_work_send (%d) and bab_test_data (%d)"
+			    " must be the same size",
+			    (int)sizeof(struct bab_work_send),
+			    (int)sizeof(bab_test_data));
+	}
+
+	memset(babinfo->bank_off, 0, sizeof(babinfo->bank_off));
+
+	buf = babinfo->buffer = 0;
+	babinfo->buf_used[buf] = 0;
+	BAB_ADD_BREAK();
+	for (i = first; i < last && i < BAB_MAXCHIPS; i++) {
+		bab_set_osc(babinfo, i);
+		bab_add_data(BAB_OSC_ADDR, babinfo->osc, sizeof(babinfo->osc));
+		bab_config_reg(BAB_ICLK_REG, BAB_ICLK_BIT(babinfo->chip_conf[i]));
+		bab_config_reg(BAB_FAST_REG, BAB_FAST_BIT(babinfo->chip_conf[i]));
+		bab_config_reg(BAB_DIV2_REG, BAB_DIV2_BIT(babinfo->chip_conf[i]));
+		bab_config_reg(BAB_SLOW_REG, BAB_SLOW_BIT(babinfo->chip_conf[i]));
+		bab_config_reg(BAB_OCLK_REG, BAB_OCLK_BIT(babinfo->chip_conf[i]));
+		for (reg = BAB_REG_CLR_FROM; reg <= BAB_REG_CLR_TO; reg++)
+			bab_config_reg(reg, false);
+		bab_add_data(BAB_COUNT_ADDR, bab_counters, sizeof(bab_counters));
+		bab_add_data(BAB_W1A_ADDR, bab_w1, sizeof(bab_w1));
+		bab_add_data(BAB_W1B_ADDR, bab_w1, sizeof(bab_w1)/2);
+		bab_add_data(BAB_W2_ADDR, bab_w2, sizeof(bab_w2));
+		babinfo->chip_off[buf][i] = babinfo->buf_used[buf] + 3;
+		bab_add_data(BAB_INP_ADDR, bab_test_data, sizeof(bab_test_data));
+		babinfo->chip_off[buf][i+1] = babinfo->buf_used[buf];
+		babinfo->bank_off[buf][bank] = babinfo->buf_used[buf];
+		babinfo->chips = i + 1;
+		bab_txrx(buf, babinfo->buf_used[buf], false);
+		babinfo->buf_used[buf] = 0;
+		BAB_ADD_BREAK();
+		for (j = first; j <= i; j++) {
+			babinfo->chip_off[buf][j] = babinfo->buf_used[buf] + 3;
+			BAB_ADD_ASYNC();
+		}
+	}
+
+	buf = babinfo->buffer = 1;
+	babinfo->buf_used[buf] = 0;
+	BAB_ADD_BREAK();
+	for (i = first; i < last && i < BAB_MAXCHIPS; i++) {
+		babinfo->chip_off[buf][i] = babinfo->buf_used[buf] + 3;
+		bab_add_data(BAB_INP_ADDR, bab_test_data, sizeof(bab_test_data));
+		BAB_ADD_ASYNC();
+	}
+	babinfo->chip_off[buf][i] = babinfo->buf_used[buf];
+	babinfo->bank_off[buf][bank] = babinfo->buf_used[buf];
+	babinfo->chips = i;
+	bab_txrx(buf, babinfo->buf_used[buf], true);
+	babinfo->buf_used[buf] = 0;
+	babinfo->chips = first;
+	for (i = first; i < last && i < BAB_MAXCHIPS; i++) {
+		uint32_t tmp[DATA_UINTS-1];
+		memcpy(tmp, babinfo->buf_read[buf]+babinfo->chip_off[buf][i], sizeof(tmp));
+		for (j = 0; j < BAB_SPI_BUFFERS; j++)
+			babinfo->chip_off[j][i] = 0;
+		for (j = 0; j < BAB_REPLY_NONCES; j++) {
+			if (tmp[j] != 0xffffffff && tmp[j] != 0x00000000) {
+				babinfo->chip_bank[i] = bank;
+				babinfo->chips = i + 1;
+				break;
+			}
+		}
+	}
+	for (i = first ; i < babinfo->chips; i++)
+		babinfo->chip_bank[i] = bank;
+}
+
+static const char *bab_modules[] = {
+	"i2c-dev",
+	"i2c-bcm2708",
+	"spidev",
+	"spi-bcm2708",
+	NULL
+};
+
+static const char *bab_memory = "/dev/mem";
+
+static int bab_memory_addr = 0x20200000;
+
+static struct {
+	int request;
+	int value;
+} bab_ioc[] = {
+	{ SPI_IOC_RD_MODE, 0 },
+	{ SPI_IOC_WR_MODE, 0 },
+	{ SPI_IOC_RD_BITS_PER_WORD, 8 },
+	{ SPI_IOC_WR_BITS_PER_WORD, 8 },
+	{ SPI_IOC_RD_MAX_SPEED_HZ, 1000000 },
+	{ SPI_IOC_WR_MAX_SPEED_HZ, 1000000 },
+	{ -1, -1 }
+};
+
+static bool bab_init_gpio(struct cgpu_info *babcgpu, struct bab_info *babinfo, int bus, int chip)
+{
+	int i, err, memfd, data;
+	char buf[64];
+
+	for (i = 0; bab_modules[i]; i++) {
+		snprintf(buf, sizeof(buf), "modprobe %s", bab_modules[i]);
+		err = system(buf);
+		if (err) {
+			applog(LOG_ERR, "%s failed to modprobe %s (%d) - you need to be root?",
+					babcgpu->drv->dname,
+					bab_modules[i], err);
+			goto bad_out;
+		}
+	}
+
+	memfd = open(bab_memory, O_RDWR | O_SYNC);
+	if (memfd < 0) {
+		applog(LOG_ERR, "%s failed open %s (%d)",
+				babcgpu->drv->dname,
+				bab_memory, errno);
+		goto bad_out;
+	}
+
+	babinfo->gpio = (volatile unsigned *)mmap(NULL, BAB_SPI_BUFSIZ,
+						  PROT_READ | PROT_WRITE,
+						  MAP_SHARED, memfd,
+						  bab_memory_addr);
+	if (babinfo->gpio == MAP_FAILED) {
+		close(memfd);
+		applog(LOG_ERR, "%s failed mmap gpio (%d)",
+				babcgpu->drv->dname,
+				errno);
+		goto bad_out;
+	}
+
+	close(memfd);
+
+	snprintf(buf, sizeof(buf), "/dev/spidev%d.%d", bus, chip);
+	babinfo->spifd = open(buf, O_RDWR);
+	if (babinfo->spifd < 0) {
+		applog(LOG_ERR, "%s failed to open spidev (%d)",
+				babcgpu->drv->dname,
+				errno);
+		goto map_out;
+	}
+
+	babcgpu->device_path = strdup(buf);
+
+	for (i = 0; bab_ioc[i].value != -1; i++) {
+		data = bab_ioc[i].value;
+		err = ioctl(babinfo->spifd, bab_ioc[i].request, (void *)&data);
+		if (err < 0) {
+			applog(LOG_ERR, "%s failed ioctl (%d) (%d)",
+					babcgpu->drv->dname,
+					i, errno);
+			goto close_out;
+		}
+	}
+
+	for (i = 0; i < BAB_MAXCHIPS; i++)
+		babinfo->chip_spis[i] = (int)((1000000.0 / (100.0 + 31.0 * (i + 1))) * 1000);
+
+	return true;
+
+close_out:
+	close(babinfo->spifd);
+	babinfo->spifd = 0;
+	free(babcgpu->device_path);
+	babcgpu->device_path = NULL;
+map_out:
+	munmap((void *)(babinfo->gpio), BAB_SPI_BUFSIZ);
+	babinfo->gpio = NULL;
+bad_out:
+	return false;
+}
+
+static void bab_init_chips(struct cgpu_info *babcgpu, struct bab_info *babinfo)
+{
+	bab_detect_chips(babcgpu, babinfo, 0, 0, BAB_MAXCHIPS);
+	memcpy(babinfo->old_conf, babinfo->chip_conf, sizeof(babinfo->old_conf));
+	memcpy(babinfo->old_fast, babinfo->chip_fast, sizeof(babinfo->old_fast));
+}
+
+static void bab_detect(bool hotplug)
+{
+	struct cgpu_info *babcgpu = NULL;
+	struct bab_info *babinfo = NULL;
+	int i;
+
+	if (hotplug)
+		return;
+
+	babcgpu = calloc(1, sizeof(*babcgpu));
+	if (unlikely(!babcgpu))
+		quithere(1, "Failed to calloc babcgpu");
+
+	babcgpu->drv = &bab_drv;
+	babcgpu->deven = DEV_ENABLED;
+	babcgpu->threads = 1;
+
+	babinfo = calloc(1, sizeof(*babinfo));
+	if (unlikely(!babinfo))
+		quithere(1, "Failed to calloc babinfo");
+	babcgpu->device_data = (void *)babinfo;
+
+	for (i = 0; i < BAB_MAXCHIPS; i++) {
+		babinfo->chip_conf[i] = BAB_DEFCONF;
+		babinfo->chip_fast[i] = BAB_DEFSPEED;
+	}
+
+	mutex_init(&babinfo->spi_lock);
+
+	if (!bab_init_gpio(babcgpu, babinfo, BAB_SPI_BUS, BAB_SPI_CHIP))
+		goto unalloc;
+
+	applog(LOG_WARNING, "%s V1 testing for %d chips ...", babcgpu->drv->dname, BAB_MAXCHIPS);
+
+	bab_init_chips(babcgpu, babinfo);
+
+	applog(LOG_WARNING, "%s found %d chips", babcgpu->drv->dname, babinfo->chips);
+
+	if (babinfo->chips == 0)
+		goto cleanup;
+
+	if (!add_cgpu(babcgpu))
+		goto cleanup;
+
+	mutex_init(&babinfo->res_lock);
+	mutex_init(&babinfo->did_lock);
+	cglock_init(&babinfo->blist_lock);
+
+	babinfo->initialised = true;
+
+	return;
+
+cleanup:
+	close(babinfo->spifd);
+	munmap((void *)(babinfo->gpio), BAB_SPI_BUFSIZ);
+unalloc:
+	mutex_destroy(&babinfo->spi_lock);
+	free(babinfo);
+	free(babcgpu);
+}
+
+static void bab_identify(__maybe_unused struct cgpu_info *babcgpu)
+{
+}
+
+#define BAB_LONG_WAIT_uS 1200000
+#define BAB_WAIT_MSG_EVERY 10
+#define BAB_LONG_WAIT_SLEEP_uS 100000
+#define BAB_STD_WAIT_uS 3000
+
+// thread to do spi txrx
+static void *bab_spi(void *userdata)
+{
+	struct cgpu_info *babcgpu = (struct cgpu_info *)userdata;
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	struct timeval start, stop;
+	double wait;
+	int i, buf, msgs;
+
+	applog(LOG_DEBUG, "%s%i: SPIing...",
+			  babcgpu->drv->name, babcgpu->device_id);
+
+	// Wait until we're ready
+	while (babcgpu->shutdown == false) {
+		if (babinfo->initialised) {
+			break;
+		}
+		cgsleep_ms(3);
+	}
+
+	msgs = 0;
+	cgtime(&start);
+	while (babcgpu->shutdown == false) {
+		buf = -1;
+		mutex_lock(&(babinfo->spi_lock));
+		for (i = 0; i < BAB_SPI_BUFFERS; i++) {
+			if (babinfo->buf_status[i] == BAB_STATE_READY) {
+				babinfo->buf_status[i] = BAB_STATE_SENDING;
+				buf = i;
+				cgtime(&start);
+				break;
+			}
+		}
+		mutex_unlock(&(babinfo->spi_lock));
+
+		if (buf == -1) {
+			cgtime(&stop);
+			wait = us_tdiff(&stop, &start);
+			if (wait > BAB_LONG_WAIT_uS) {
+				if ((msgs++ % BAB_WAIT_MSG_EVERY) == 0) {
+					applog(LOG_WARNING, "%s%i: SPI waiting %.0fus ...",
+								babcgpu->drv->name,
+								babcgpu->device_id,
+								(float)wait);
+				}
+			}
+			cgsleep_us(BAB_LONG_WAIT_SLEEP_uS);
+			continue;
+		}
+
+		bab_txrx(buf, babinfo->buf_used[buf], false);
+		cgtime(&stop);
+		wait = us_tdiff(&stop, &start);
+		if (wait < BAB_STD_WAIT_uS)
+			cgsleep_us((uint64_t)(BAB_STD_WAIT_uS - wait));
+		else if (wait > BAB_LONG_WAIT_uS) {
+			applog(LOG_DEBUG, "%s%i: SPI waited %.0fus",
+					  babcgpu->drv->name, babcgpu->device_id,
+					  (float)wait);
+		}
+
+		mutex_lock(&(babinfo->spi_lock));
+		babinfo->buf_status[i] = BAB_STATE_SENT;
+		mutex_unlock(&(babinfo->spi_lock));
+		msgs = 0;
+	}
+
+	return NULL;
+}
+
+static void bab_flush_work(struct cgpu_info *babcgpu)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+
+	applog(LOG_DEBUG, "%s%i: flushing work",
+			  babcgpu->drv->name, babcgpu->device_id);
+
+	mutex_lock(&(babinfo->did_lock));
+	memset(&(babinfo->last_did), 0, sizeof(babinfo->last_did));
+	mutex_unlock(&(babinfo->did_lock));
+}
+
+static void ms3steps(uint32_t *p)
+{
+	uint32_t a, b, c, d, e, f, g, h, new_e, new_a;
+	int i;
+
+	a = p[0];
+	b = p[1];
+	c = p[2];
+	d = p[3];
+	e = p[4];
+	f = p[5];
+	g = p[6];
+	h = p[7];
+	for (i = 0; i < 3; i++) {
+		new_e = p[i+16] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) + d;
+		new_a = p[i+16] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) +
+			SHA256_F1(a) + MAJ(a,b,c);
+		d = c;
+		c = b;
+		b = a;
+		a = new_a;
+		h = g;
+		g = f;
+		f = e;
+		e = new_e;
+	}
+	p[15] = a;
+	p[14] = b;
+	p[13] = c;
+	p[12] = d;
+	p[11] = e;
+	p[10] = f;
+	p[9] = g;
+	p[8] = h;
+}
+
+#define DATA_MERKLE7 16
+#define DATA_NTIME 17
+#define DATA_BITS 18
+#define DATA_NONCE 19
+
+#define WORK_MERKLE7 (16*4)
+#define WORK_NTIME (17*4)
+#define WORK_BITS (18*4)
+#define WORK_NONCE (19*4)
+
+static uint32_t decnonce(uint32_t in)
+{
+	uint32_t out;
+
+	/* First part load */
+	out = (in & 0xFF) << 24;
+	in >>= 8;
+
+	/* Byte reversal */
+	in = (((in & 0xaaaaaaaa) >> 1) | ((in & 0x55555555) << 1));
+	in = (((in & 0xcccccccc) >> 2) | ((in & 0x33333333) << 2));
+	in = (((in & 0xf0f0f0f0) >> 4) | ((in & 0x0f0f0f0f) << 4));
+
+	out |= (in >> 2) & 0x3FFFFF;
+
+	/* Extraction */
+	if (in & 1)
+		out |= (1 << 23);
+	if (in & 2)
+		out |= (1 << 22);
+
+	out -= 0x800004;
+	return out;
+}
+
+/*
+ * Find the matching work item by checking the nonce against each work
+ * item for the chip
+ * Discard any work items older than a match
+ */
+static bool oknonce(struct thr_info *thr, struct cgpu_info *babcgpu, int chip, uint32_t nonce)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	BLIST *bitem;
+	unsigned int links, tests;
+	int i;
+
+	babinfo->chip_nonces[chip]++;
+
+	nonce = decnonce(nonce);
+
+	/*
+	 * We can grab the head of the chip work queue and then
+	 * release the lock and follow it to the end
+	 * since the other thread will only add items above the
+	 * head - it wont touch the list->next pointers from the
+	 * head to the end - only the head->prev pointer may get
+	 * changed
+	 */
+	cg_rlock(&babinfo->blist_lock);
+	bitem = babinfo->chip_list[chip];
+	cg_runlock(&babinfo->blist_lock);
+
+	if (!bitem) {
+		applog(LOG_ERR, "%s%i: chip %d has no work!",
+				babcgpu->drv->name, babcgpu->device_id, chip);
+		babinfo->untested_nonces++;
+		return false;
+	}
+
+	babinfo->tested_nonces++;
+
+	tests = 0;
+	links = 0;
+	while (bitem) {
+		if (!bitem->work) {
+			applog(LOG_ERR, "%s%i: chip %d bitem links %d has no work!",
+					babcgpu->drv->name,
+					babcgpu->device_id,
+					chip, links);
+		} else {
+			for (i = 0; i < BAB_NONCE_OFFSETS; i++) {
+				tests++;
+				if (test_nonce(bitem->work, nonce + bab_nonce_offsets[i])) {
+					submit_tested_work(thr, bitem->work);
+					babinfo->nonce_offset_count[i]++;
+					babinfo->chip_good[chip]++;
+					bitem->nonces++;
+					babinfo->new_nonces++;
+					babinfo->ok_nonces++;
+					free_blist(babcgpu, bitem->next, chip);
+					babinfo->total_tests += tests;
+					if (babinfo->max_tests_per_nonce < tests)
+						babinfo->max_tests_per_nonce = tests;
+					babinfo->total_links += links;
+					if (babinfo->max_links < links)
+						babinfo->max_links = links;
+					return true;
+				}
+			}
+		}
+		bitem = bitem->next;
+		links++;
+	}
+
+	if (babinfo->not_first_reply[chip]) {
+		babinfo->chip_bad[chip]++;
+		inc_hw_errors(thr);
+	} else
+		babinfo->initial_ignored++;
+
+	return false;
+}
+
+// Results checking thread
+static void *bab_res(void *userdata)
+{
+	struct cgpu_info *babcgpu = (struct cgpu_info *)userdata;
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	struct thr_info *thr = babcgpu->thr[0];
+	bool first_second;
+	uint32_t nonce;
+	int chip;
+
+	applog(LOG_DEBUG, "%s%i: Results...",
+			  babcgpu->drv->name, babcgpu->device_id);
+
+	// Wait until we're ready
+	while (babcgpu->shutdown == false) {
+		if (babinfo->initialised) {
+			break;
+		}
+		cgsleep_ms(3);
+	}
+
+	while (babcgpu->shutdown == false) {
+		if (!oldest_nonce(babcgpu, &chip, &nonce, &first_second)) {
+			cgsleep_ms(3);
+			continue;
+		}
+
+		if (first_second)
+			babinfo->not_first_reply[chip] = true;
+
+		oknonce(thr, babcgpu, chip, nonce);
+	}
+
+	return NULL;
+}
+
+static bool bab_do_work(struct cgpu_info *babcgpu)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	int busy, newbusy, match, work_items = 0;
+	int spi, mis, miso;
+	int i, j;
+	BLIST *bitem;
+	bool res, got_a_nonce;
+
+	for (i = 0; i < babinfo->chips; i++) {
+		bitem = next_work(babcgpu, i);
+		if (!bitem) {
+			applog(LOG_ERR, "%s%i: short work list (%i) expected %d - discarded",
+					babcgpu->drv->name, babcgpu->device_id,
+					i, babinfo->chips);
+			for (j = 0; j < i; i++)
+				discard_last(babcgpu, j);
+
+			return false;
+		}
+		memcpy((void *)&(babinfo->chip_input[i].midstate[0]),
+			bitem->work->midstate, sizeof(bitem->work->midstate));
+		memcpy((void *)&(babinfo->chip_input[i].merkle7),
+			(void *)&(bitem->work->data[WORK_MERKLE7]), 12);
+
+		ms3steps((void *)&(babinfo->chip_input[i]));
+		work_items++;
+	}
+
+	// Send
+	res = bab_put(babcgpu, babinfo);
+	if (!res) {
+		applog(LOG_DEBUG, "%s%i: couldn't put work ...",
+				  babcgpu->drv->name, babcgpu->device_id);
+	}
+
+	// Receive
+	res = bab_get(babcgpu, babinfo);
+	if (!res) {
+		applog(LOG_DEBUG, "%s%i: didn't get work reply ...",
+				  babcgpu->drv->name, babcgpu->device_id);
+		return false;
+	}
+
+	applog(LOG_DEBUG, "%s%i: Did get work reply ...",
+			  babcgpu->drv->name, babcgpu->device_id);
+
+	spi = mis = miso = 0;
+
+	for (i = 0; i < babinfo->chips; i++) {
+		match = 0;
+		newbusy = busy = babinfo->chip_busy[i];
+
+		if (!babinfo->chip_conf[i])
+			continue;
+
+		for (j = 1; j < BAB_REPLY_NONCES; j++) {
+			if (babinfo->chip_results[i].nonce[(busy+j) % BAB_REPLY_NONCES] !=
+			    babinfo->chip_prev[i].nonce[(busy+j) % BAB_REPLY_NONCES])
+				newbusy = (busy+j) % BAB_REPLY_NONCES;
+			else
+				match++;
+		}
+
+		if (!match) {
+			if (!miso) {
+				mis++;
+// ignore for now ...				babinfo->chip_miso[i]++;
+			}
+			miso = 1;
+			continue;
+		}
+
+		miso = 0;
+		if (babinfo->chip_results[i].jobsel != 0xffffffff &&
+		    babinfo->chip_results[i].jobsel != 0x00000000) {
+			spi++;
+			babinfo->chip_spie[i]++;
+			applog(LOG_DEBUG, "%s%i: SPI ERROR on chip %d (0x%08x)",
+					  babcgpu->drv->name, babcgpu->device_id,
+					  i, babinfo->chip_results[i].jobsel);
+		}
+
+// Not used yet
+//		if (babinfo->chip_results[i].jobsel != babinfo->chip_prev[i].jobsel) {
+
+		got_a_nonce = false;
+		for (; newbusy != busy; busy = (busy + 1) % BAB_REPLY_NONCES) {
+			if (babinfo->chip_results[i].nonce[busy] == 0xffffffff ||
+			    babinfo->chip_results[i].nonce[busy] == 0x00000000) {
+				babinfo->chip_results[i].nonce[busy] = babinfo->chip_prev[i].nonce[busy];
+				spi = 1;
+				continue;
+			}
+
+			store_nonce(babcgpu, i,
+				    babinfo->chip_results[i].nonce[busy],
+				    babinfo->nonce_before[i]);
+
+			got_a_nonce = true;
+		}
+
+		/*
+		 * We only care about this after the first reply we find a nonce
+		 * After that, the value has no more effect
+		 */
+		if (got_a_nonce)
+			babinfo->nonce_before[i] = true;
+
+		mis += miso;
+		babinfo->chip_miso[i] += miso;
+		babinfo->chip_busy[i] = busy;
+	}
+
+	memcpy((void *)(&(babinfo->chip_prev[0])),
+		(void *)(&(babinfo->chip_results[0])),
+		sizeof(babinfo->chip_prev));
+
+	applog(LOG_DEBUG, "Work: items:%d spi:%d miso:%d",
+			  work_items, spi, mis);
+
+	return true;
+}
+
+static bool bab_thread_prepare(struct thr_info *thr)
+{
+	struct cgpu_info *babcgpu = thr->cgpu;
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+
+	if (thr_info_create(&(babinfo->spi_thr), NULL, bab_spi, (void *)babcgpu)) {
+		applog(LOG_ERR, "%s%i: SPI thread create failed",
+				babcgpu->drv->name, babcgpu->device_id);
+		return false;
+	}
+	pthread_detach(babinfo->spi_thr.pth);
+
+	/*
+	 * We require a seperate results checking thread since there is a lot
+	 * of work done checking the results multiple times - thus we don't
+	 * want that delay affecting sending/receiving work to/from the device
+	 */
+	if (thr_info_create(&(babinfo->res_thr), NULL, bab_res, (void *)babcgpu)) {
+		applog(LOG_ERR, "%s%i: Results thread create failed",
+				babcgpu->drv->name, babcgpu->device_id);
+		return false;
+	}
+	pthread_detach(babinfo->res_thr.pth);
+
+	return true;
+}
+
+static void bab_shutdown(struct thr_info *thr)
+{
+	struct cgpu_info *babcgpu = thr->cgpu;
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	int i;
+
+	applog(LOG_DEBUG, "%s%i: shutting down",
+			  babcgpu->drv->name, babcgpu->device_id);
+
+	for (i = 0; i < babinfo->chips; i++)
+// TODO:	bab_shutdown(babcgpu, babinfo, i);
+		;
+
+	babcgpu->shutdown = true;
+}
+
+static bool bab_queue_full(struct cgpu_info *babcgpu)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	struct work *work;
+	bool ret;
+
+	if (babinfo->work_count >= babinfo->chips)
+		ret = true;
+	else {
+		work = get_queued(babcgpu);
+		if (work)
+			store_work(babcgpu, work);
+		else
+			// Avoid a hard loop when we can't get work fast enough
+			cgsleep_ms(10);
+
+		ret = false;
+	}
+
+	return ret;
+}
+
+/*
+ * 1.0s per nonce = 4.2GH/s
+ * So anything around 4GH/s or less per chip should be fine
+ */
+#define BAB_STD_WORK_uS 1000000
+
+#define BAB_STD_DELAY_uS 30000
+
+/*
+ * TODO: allow this to run through more than once - the second+
+ * time not sending any new work unless a flush occurs since:
+ * at the moment we have BAB_STD_WORK_uS latency added to earliest replies
+ */
+static int64_t bab_scanwork(__maybe_unused struct thr_info *thr)
+{
+	struct cgpu_info *babcgpu = thr->cgpu;
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	int64_t hashcount = 0;
+	struct timeval now;
+	double delay;
+
+	bab_do_work(babcgpu);
+
+	// Sleep now so we get the work "bab_queue_full()" just before we use it
+	while (80085) {
+		cgtime(&now);
+		mutex_lock(&(babinfo->did_lock));
+		delay = us_tdiff(&now, &(babinfo->last_did));
+		mutex_unlock(&(babinfo->did_lock));
+		if (delay < (BAB_STD_WORK_uS - BAB_STD_DELAY_uS))
+			cgsleep_us(BAB_STD_DELAY_uS);
+		else
+			break;
+	}
+
+	if (babinfo->new_nonces) {
+		hashcount += 0xffffffffull * babinfo->new_nonces;
+		babinfo->new_nonces = 0;
+	}
+
+	return hashcount;
+}
+
+#define CHIPS_PER_STAT 16
+
+static struct api_data *bab_api_stats(struct cgpu_info *babcgpu)
+{
+	struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data);
+	struct api_data *root = NULL;
+	char data[2048];
+	char buf[32];
+	int i, to, j;
+
+	if (babinfo->initialised == false)
+		return NULL;
+
+	root = api_add_int(root, "Chips", &(babinfo->chips), true);
+
+	for (i = 0; i < babinfo->chips; i += CHIPS_PER_STAT) {
+		to = i + CHIPS_PER_STAT - 1;
+		if (to >= babinfo->chips)
+			to = babinfo->chips - 1;
+
+		data[0] = '\0';
+		for (j = i; j <= to; j++) {
+			snprintf(buf, sizeof(buf),
+					"%s%"PRIu64,
+					j == i ? "" : " ",
+					babinfo->chip_nonces[j]);
+			strcat(data, buf);
+		}
+		snprintf(buf, sizeof(buf), "Nonces %d - %d", i, to);
+		root = api_add_string(root, buf, data, true);
+
+		data[0] = '\0';
+		for (j = i; j <= to; j++) {
+			snprintf(buf, sizeof(buf),
+					"%s%"PRIu64,
+					j == i ? "" : " ",
+					babinfo->chip_good[j]);
+			strcat(data, buf);
+		}
+		snprintf(buf, sizeof(buf), "Good %d - %d", i, to);
+		root = api_add_string(root, buf, data, true);
+
+		data[0] = '\0';
+		for (j = i; j <= to; j++) {
+			snprintf(buf, sizeof(buf),
+					"%s%"PRIu64,
+					j == i ? "" : " ",
+					babinfo->chip_bad[j]);
+			strcat(data, buf);
+		}
+		snprintf(buf, sizeof(buf), "Bad %d - %d", i, to);
+		root = api_add_string(root, buf, data, true);
+
+		data[0] = '\0';
+		for (j = i; j <= to; j++) {
+			snprintf(buf, sizeof(buf),
+					"%s0x%02x",
+					j == i ? "" : " ",
+					(int)(babinfo->chip_conf[j]));
+			strcat(data, buf);
+		}
+		snprintf(buf, sizeof(buf), "Conf %d - %d", i, to);
+		root = api_add_string(root, buf, data, true);
+
+		data[0] = '\0';
+		for (j = i; j <= to; j++) {
+			snprintf(buf, sizeof(buf),
+					"%s0x%02x",
+					j == i ? "" : " ",
+					(int)(babinfo->chip_fast[j]));
+			strcat(data, buf);
+		}
+		snprintf(buf, sizeof(buf), "Fast %d - %d", i, to);
+		root = api_add_string(root, buf, data, true);
+	}
+
+	for (i = 0; i < BAB_NONCE_OFFSETS; i++) {
+		snprintf(buf, sizeof(buf), "Nonce Offset 0x%08x", bab_nonce_offsets[i]);
+		root = api_add_uint64(root, buf, &(babinfo->nonce_offset_count[i]), true);
+	}
+
+	root = api_add_uint64(root, "Tested", &(babinfo->tested_nonces), true);
+	root = api_add_uint64(root, "Total Tests", &(babinfo->total_tests), true);
+	root = api_add_uint64(root, "Max Tests", &(babinfo->max_tests_per_nonce), true);
+	float avg = babinfo->tested_nonces ? (float)(babinfo->total_tests) /
+					     (float)(babinfo->tested_nonces) : 0;
+// TODO: add a API_AVG which is 3 places - double/float?
+	root = api_add_volts(root, "Avg Tests", &avg, true);
+	root = api_add_uint64(root, "Untested", &(babinfo->untested_nonces), true);
+
+	root = api_add_uint64(root, "Work Links", &(babinfo->total_links), true);
+	root = api_add_uint64(root, "Max Links", &(babinfo->max_links), true);
+	avg = babinfo->tested_nonces ? (float)(babinfo->total_links) /
+					(float)(babinfo->tested_nonces) : 0;
+	root = api_add_volts(root, "Avg Links", &avg, true);
+
+	root = api_add_uint32(root, "Initial Ignored", &(babinfo->initial_ignored), true);
+
+	root = api_add_int(root, "BList Count", &(babinfo->blist_count), true);
+	root = api_add_int(root, "BFree Count", &(babinfo->bfree_count), true);
+	root = api_add_int(root, "Work Count", &(babinfo->work_count), true);
+	root = api_add_int(root, "Chip Count", &(babinfo->chip_count), true);
+
+	root = api_add_int(root, "RList Count", &(babinfo->rlist_count), true);
+	root = api_add_int(root, "RFree Count", &(babinfo->rfree_count), true);
+	root = api_add_int(root, "Result Count", &(babinfo->res_count), true);
+
+	return root;
+}
+#endif
+
+struct device_drv bab_drv = {
+	.drv_id = DRIVER_bab,
+	.dname = "BlackArrowBitFuryGPIO",
+	.name = "BaB",
+	.drv_detect = bab_detect,
+#ifdef LINUX
+	.get_api_stats = bab_api_stats,
+//TODO:	.get_statline_before = get_bab_statline_before,
+	.identify_device = bab_identify,
+	.thread_prepare = bab_thread_prepare,
+	.hash_work = hash_queued_work,
+	.scanwork = bab_scanwork,
+	.queue_full = bab_queue_full,
+	.flush_work = bab_flush_work,
+	.thread_shutdown = bab_shutdown
+#endif
+};

+ 1 - 0
miner.h

@@ -249,6 +249,7 @@ static inline int fsync (int fd)
 	DRIVER_ADD_COMMAND(hashfast) \
 	DRIVER_ADD_COMMAND(klondike) \
 	DRIVER_ADD_COMMAND(knc) \
+	DRIVER_ADD_COMMAND(bab) \
 	DRIVER_ADD_COMMAND(avalon)
 
 #define DRIVER_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \