Browse Source

BlackArrowBitfury early GPIO V1 driver

Kano 12 years ago
parent
commit
811da2efaf
1 changed files with 1859 additions and 1 deletions
  1. 1859 1
      driver-bab.c

+ 1859 - 1
driver-bab.c

@@ -1,14 +1,1872 @@
+/*
+ * Copyright 2013 Andrew Smith
+ * Copyright 2013 bitfury
+ *
+ * Details for BitFury GPIO development of this driver are from
+ * the chainminer code: https://github.com/bfsb/chainminer
+ * (coz there seems to be no good documentation of it anywhere ...)
+ * Thanks to the developer of chainminer for providing the source
+ * to base the first version of the gpio/spi code in this BitFury chip
+ * driver :)
+ *
+ * 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 "config.h"
 #include "compat.h"
 #include "compat.h"
 #include "miner.h"
 #include "miner.h"
+#include "sha2.h"
+
+/*
+ * This code has been tested on RPi running Raspbian
+ * with a BlackArrow BitFury V1 16 chip GPIO board
+ */
 
 
+#ifndef LINUX
 static void bab_detect(__maybe_unused bool hotplug)
 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++)
+//		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 = {
 struct device_drv bab_drv = {
 	.drv_id = DRIVER_bab,
 	.drv_id = DRIVER_bab,
 	.dname = "BlackArrowBitFuryGPIO",
 	.dname = "BlackArrowBitFuryGPIO",
 	.name = "BaB",
 	.name = "BaB",
-	.drv_detect = bab_detect
+	.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
 };
 };