Browse Source

Merge branch 'zeusminer' into bfgminer

Luke Dashjr 11 years ago
parent
commit
42d427444f
8 changed files with 387 additions and 82 deletions
  1. 1 0
      AUTHORS
  2. 5 0
      Makefile.am
  3. 23 0
      configure.ac
  4. 76 80
      driver-icarus.c
  5. 21 2
      driver-icarus.h
  6. 246 0
      driver-zeusminer.c
  7. 12 0
      miner.h
  8. 3 0
      util.h

+ 1 - 0
AUTHORS

@@ -3,6 +3,7 @@ GPU mining and refactor: Con Kolivas <kernel@kolivas.org> 15qSxP1SQcUX3o4nhkfdbg
 AntMiner driver: Nate Woolls <nwoolls@gmail.com> and Lingchao Xu <lingchao.xu@bitmaintech.com>
 DualMiner driver: Nate Woolls <nwoolls@gmail.com> and Dualminer Team <dualminer@broadeng.net>
 GridSeed driver: Nate Woolls <nwoolls@gmail.com> and GridSeed Team <develop@gridseed.com>
+ZeusMiner driver: Nate Woolls <nwoolls@gmail.com> and ZeusMiner Team <cs@zeusminer.com>
 Bitfury GPIO-based drivers: Bitfury and Anatoly Legkodymov <legko777@fastmail.fm>
 Big Picture Mining and TwinFury drivers: Andreas Auer <aauer1@gmail.com>
 Avalon and Icarus drivers: Xiangfu <xiangfu@openmobilefree.net>

+ 5 - 0
Makefile.am

@@ -2,6 +2,7 @@
 # Copyright 2012 zefir
 # Copyright 2011-2013 Con Kolivas
 # Copyright 2013 James Z.M. Gao
+# Copyright 2013-2014 Nate Woolls
 #
 # 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
@@ -257,6 +258,10 @@ if USE_DUALMINER
 bfgminer_SOURCES += driver-dualminer.c
 endif
 
+if USE_ZEUSMINER
+bfgminer_SOURCES += driver-zeusminer.c
+endif
+
 if HAS_AVALON
 bfgminer_SOURCES += driver-avalon.c driver-avalon.h hexdump.c
 endif

+ 23 - 0
configure.ac

@@ -4,6 +4,7 @@ dnl * Copyright 2010-2011 Jeff Garzik
 dnl * Copyright 2012 Xiangfu
 dnl * Copyright 2011 Rusty Russell
 dnl * Copyright 2011 Mark Crichton
+dnl * Copyright 2013-2014 Nate Woolls
 dnl *
 dnl * This program is free software; you can redistribute it and/or modify it
 dnl * under the terms of the GNU General Public License as published by the Free
@@ -479,6 +480,28 @@ if test "x$dualminer" = "xyes"; then
 fi
 AM_CONDITIONAL([USE_DUALMINER], [test x$dualminer = xyes])
 
+driverlist="$driverlist zeusminer"
+AC_ARG_ENABLE([zeusminer],
+	[AC_HELP_STRING([--disable-zeusminer],[Compile support for ZeusMiner (default enabled with scrypt)])],
+	[zeusminer=$enableval],
+	[zeusminer=$ddauto])
+if test "x$zeusminer" = "xno"; then
+	true
+elif test "x$icarus$scrypt" = "xyesyes"; then
+	zeusminer=yes
+elif test "x$zeusminer" = "xauto"; then
+	zeusminer=no
+elif test "x$scrypt" = "xno"; then
+	AC_MSG_ERROR([You explicitly enabled ZeusMiner, but did not enable scrypt])
+elif test "x$icarus" = "xno"; then
+	AC_MSG_ERROR([You explicitly disabled Icarus and explicitly enabled ZeusMiner])
+fi
+if test "x$zeusminer" = "xyes"; then
+	AC_DEFINE([USE_ZEUSMINER], [1], [Defined to 1 if ZeusMiner support is wanted])
+	has_asic=yes
+fi
+AM_CONDITIONAL([USE_ZEUSMINER], [test x$zeusminer = xyes])
+
 driverlist="$driverlist gridseed"
 AC_ARG_ENABLE([gridseed],
 	[AC_HELP_STRING([--disable-gridseed],[Compile support for GridSeed (default enabled with scrypt)])],

+ 76 - 80
driver-icarus.c

@@ -413,27 +413,7 @@ const char *icarus_set_timing(struct cgpu_info * const proc, const char * const
 
 static uint32_t mask(int work_division)
 {
-	uint32_t nonce_mask = 0x7fffffff;
-
-	// yes we can calculate these, but this way it's easy to see what they are
-	switch (work_division) {
-	case 1:
-		nonce_mask = 0xffffffff;
-		break;
-	case 2:
-		nonce_mask = 0x7fffffff;
-		break;
-	case 4:
-		nonce_mask = 0x3fffffff;
-		break;
-	case 8:
-		nonce_mask = 0x1fffffff;
-		break;
-	default:
-		quit(1, "Invalid2 work_division (%d) must be 1, 2, 4 or 8", work_division);
-	}
-
-	return nonce_mask;
+	return 0xffffffff / work_division;
 }
 
 // Number of bytes remaining after reading a nonce from Icarus
@@ -512,42 +492,47 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 	int ob_size = strlen(info->golden_ob) / 2;
 	unsigned char ob_bin[ob_size];
 	BFGINIT(info->ob_size, ob_size);
-	
-	hex2bin(ob_bin, info->golden_ob, sizeof(ob_bin));
-	icarus_write(fd, ob_bin, sizeof(ob_bin));
-	cgtime(&tv_start);
-
-	memset(nonce_bin, 0, sizeof(nonce_bin));
-	// Do not use info->read_size here, instead read exactly ICARUS_NONCE_SIZE
-	// We will then compare the bytes left in fd with info->read_size to determine
-	// if this is a valid device
-	icarus_gets(nonce_bin, fd, &tv_finish, NULL, info->probe_read_count, ICARUS_NONCE_SIZE);
-	
-	// How many bytes were left after reading the above nonce
-	int bytes_left = icarus_excess_nonce_size(fd, info);
-	
-	icarus_close(fd);
 
-	bin2hex(nonce_hex, nonce_bin, sizeof(nonce_bin));
-	if (strncmp(nonce_hex, info->golden_nonce, 8))
+	if (!info->ignore_golden_nonce)
 	{
-		applog(LOG_DEBUG,
-			"%s: "
-			"Test failed at %s: get %s, should: %s",
-			api->dname,
-			devpath, nonce_hex, info->golden_nonce);
-		return false;
-	}
+		hex2bin(ob_bin, info->golden_ob, sizeof(ob_bin));
+		icarus_write(fd, ob_bin, sizeof(ob_bin));
+		cgtime(&tv_start);
 		
-	if (info->read_size - ICARUS_NONCE_SIZE != bytes_left) 
-	{
-		applog(LOG_DEBUG,
-			   "%s: "
-			   "Test failed at %s: expected %d bytes, got %d",
-			   api->dname,
-			   devpath, info->read_size, ICARUS_NONCE_SIZE + bytes_left);
-		return false;
+		memset(nonce_bin, 0, sizeof(nonce_bin));
+		// Do not use info->read_size here, instead read exactly ICARUS_NONCE_SIZE
+		// We will then compare the bytes left in fd with info->read_size to determine
+		// if this is a valid device
+		icarus_gets(nonce_bin, fd, &tv_finish, NULL, info->probe_read_count, ICARUS_NONCE_SIZE);
+		
+		// How many bytes were left after reading the above nonce
+		int bytes_left = icarus_excess_nonce_size(fd, info);
+		
+		icarus_close(fd);
+		
+		bin2hex(nonce_hex, nonce_bin, sizeof(nonce_bin));
+		if (strncmp(nonce_hex, info->golden_nonce, 8))
+		{
+			applog(LOG_DEBUG,
+				   "%s: "
+				   "Test failed at %s: get %s, should: %s",
+				   api->dname,
+				   devpath, nonce_hex, info->golden_nonce);
+			return false;
+		}
+		
+		if (info->read_size - ICARUS_NONCE_SIZE != bytes_left)
+		{
+			applog(LOG_DEBUG,
+				   "%s: "
+				   "Test failed at %s: expected %d bytes, got %d",
+				   api->dname,
+				   devpath, info->read_size, ICARUS_NONCE_SIZE + bytes_left);
+			return false;
+		}
 	}
+	else
+		icarus_close(fd);
 	
 	applog(LOG_DEBUG,
 		"%s: "
@@ -704,6 +689,8 @@ bool icarus_init(struct thr_info *thr)
 	if (!info->fpga_count)
 		info->fpga_count = info->work_division;
 	
+	if (!is_power_of_two(info->work_division))
+		info->work_division = upper_power_of_two_u32(info->work_division);
 	info->nonce_mask = mask(info->work_division);
 	
 	return true;
@@ -894,7 +881,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	double Hs, W, fullnonce;
 	int read_count;
 	bool limited;
-	int64_t estimate_hashes;
 	uint32_t values;
 	int64_t hash_count_range;
 
@@ -1038,46 +1024,56 @@ keepwaiting:
 
 	// OK, done starting Icarus's next job... now process the last run's result!
 
-	// aborted before becoming idle, get new work
-	if (ret == ICA_GETS_TIMEOUT || ret == ICA_GETS_RESTART) {
+	if (ret == ICA_GETS_OK && !was_hw_error)
+	{
+		submit_nonce(thr, nonce_work, nonce);
+		
 		icarus_transition_work(state, work);
-		// ONLY up to just when it aborted
-		// We didn't read a reply so we don't subtract ICARUS_READ_TIME
-		estimate_hashes = ((double)(elapsed.tv_sec)
-					+ ((double)(elapsed.tv_usec))/((double)1000000)) / info->Hs;
+		
+		hash_count = (nonce & info->nonce_mask);
+		hash_count++;
+		hash_count *= info->fpga_count;
+
+		applog(LOG_DEBUG, "%"PRIpreprv" nonce = 0x%08x = 0x%08" PRIx64 " hashes (%"PRId64".%06lus)",
+		       icarus->proc_repr,
+		       nonce,
+		       (uint64_t)hash_count,
+		       (int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
+	}
+	else
+	{
+		double estimate_hashes = elapsed.tv_sec;
+		estimate_hashes += ((double)elapsed.tv_usec) / 1000000.;
+		
+		if (ret == ICA_GETS_OK)
+		{
+			inc_hw_errors(thr, state->last_work, nonce);
+			estimate_hashes -= ICARUS_READ_TIME(info->baud, info->read_size);
+		}
+		
+		icarus_transition_work(state, work);
+		
+		estimate_hashes /= info->Hs;
 
 		// If some Serial-USB delay allowed the full nonce range to
 		// complete it can't have done more than a full nonce
 		if (unlikely(estimate_hashes > 0xffffffff))
 			estimate_hashes = 0xffffffff;
 
-		applog(LOG_DEBUG, "%"PRIpreprv" no nonce = 0x%08"PRIx64" hashes (%"PRId64".%06lus)",
+		applog(LOG_DEBUG, "%"PRIpreprv" %s nonce = 0x%08"PRIx64" hashes (%"PRId64".%06lus)",
 		       icarus->proc_repr,
+		       (ret == ICA_GETS_OK) ? "bad" : "no",
 		       (uint64_t)estimate_hashes,
 		       (int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
 
 		hash_count = estimate_hashes;
-		goto out;
+		
+		if (ret != ICA_GETS_OK)
+			goto out;
 	}
 
 	// Only ICA_GETS_OK gets here
 	
-	if (likely(!was_hw_error))
-		submit_nonce(thr, nonce_work, nonce);
-	else
-		inc_hw_errors(thr, state->last_work, nonce);
-	icarus_transition_work(state, work);
-
-	hash_count = (nonce & info->nonce_mask);
-	hash_count++;
-	hash_count *= info->fpga_count;
-
-	applog(LOG_DEBUG, "%"PRIpreprv" nonce = 0x%08x = 0x%08" PRIx64 " hashes (%"PRId64".%06lus)",
-	       icarus->proc_repr,
-	       nonce,
-	       (uint64_t)hash_count,
-	       (int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
-
 	if (info->do_default_detection && elapsed.tv_sec >= DEFAULT_DETECT_THRESHOLD) {
 		int MHs = (double)hash_count / ((double)elapsed.tv_sec * 1e6 + (double)elapsed.tv_usec);
 		--info->do_default_detection;
@@ -1282,8 +1278,8 @@ const char *icarus_set_work_division(struct cgpu_info * const proc, const char *
 {
 	struct ICARUS_INFO * const info = proc->device_data;
 	const int work_division = atoi(newvalue);
-	if (!(work_division == 1 || work_division == 2 || work_division == 4 || work_division == 8))
-		return "Invalid work_division: must be 1, 2, 4 or 8";
+	if (!is_power_of_two(work_division))
+		return "Invalid work_division: must be a power of two";
 	if (info->user_set & IUS_FPGA_COUNT)
 	{
 		if (info->fpga_count > work_division)

+ 21 - 2
driver-icarus.h

@@ -70,13 +70,16 @@ struct ICARUS_INFO {
 	// time to calculate the golden_ob
 	struct timeval golden_tv;
 
+	// History structures for calculating read_count
+	// when info->do_icarus_timing is true
 	struct ICARUS_HISTORY history[INFO_HISTORY+1];
 	uint32_t min_data_count;
 
-	// seconds per Hash
-	double Hs;
+	// Timeout scanning for a nonce (deciseconds)
 	int read_count;
+	// Timeout scanning for a golden nonce (deciseconds)
 	int probe_read_count;
+	
 	// ds limit for (short=/long=) read_count
 	int read_count_limit;
 
@@ -97,9 +100,16 @@ struct ICARUS_INFO {
 
 	// icarus-options
 	int baud;
+	
+	// Used to calculate / display hash count when nonce is NOT found
+	// seconds per Hash
+	double Hs;
+	
+	// Used to calculate / display hash count when a nonce is found
 	int work_division;
 	int fpga_count;
 	uint32_t nonce_mask;
+	
 	enum icarus_reopen_mode reopen_mode;
 	bool reopen_now;
 	uint8_t user_set;
@@ -111,14 +121,23 @@ struct ICARUS_INFO {
 	// Bytes to read from Icarus for nonce
 	int read_size;
 	
+	// Settings used when probing / detecting
 	size_t ob_size;
 	const char *golden_ob;
 	const char *golden_nonce;
 	bool nonce_littleendian;
+	// Don't check the golden nonce returned when probing
+	bool ignore_golden_nonce;
 	
 	// Custom driver functions
 	bool (*detect_init_func)(const char *devpath, int fd, struct ICARUS_INFO *);
 	bool (*job_start_func)(struct thr_info *);
+	
+#ifdef USE_ZEUSMINER
+	// Hardware information, doesn't affect anything directly
+	uint16_t freq;
+	uint8_t chips;
+#endif
 };
 
 struct icarus_state {

+ 246 - 0
driver-zeusminer.c

@@ -0,0 +1,246 @@
+/*
+ * Copyright 2014 Nate Woolls
+ * Copyright 2014 ZeusMiner Team
+ * Copyright 2014 Luke Dashjr
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+
+#include "miner.h"
+#include "driver-icarus.h"
+#include "lowl-vcom.h"
+
+#define ZEUSMINER_IO_SPEED          115200
+
+#define ZEUSMINER_CHIP_GEN1_CORES   8
+#define ZEUSMINER_CHIP_CORES        ZEUSMINER_CHIP_GEN1_CORES
+#define ZEUSMINER_CHIPS_COUNT_MAX   1
+#define ZEUSMINER_CHIPS_COUNT       6
+#define ZEUSMINER_DEFAULT_CLOCK     328
+#define ZEUSMINER_MIN_CLOCK         200
+#define ZEUSMINER_MAX_CLOCK         383
+
+
+BFG_REGISTER_DRIVER(zeusminer_drv)
+
+static
+const struct bfg_set_device_definition zeusminer_set_device_funcs[];
+
+// device helper functions
+
+static
+uint32_t zeusminer_calc_clk_header(uint16_t freq)
+{
+	//set clk_reg based on chip_clk
+	uint32_t clk_reg = (uint32_t)freq * 2 / 3;
+	
+	//clock speed mask for header
+	uint32_t clk_header = (clk_reg << 24) + ((0xff - clk_reg) << 16);
+	
+	return clk_header;
+}
+
+// ICARUS_INFO functions - driver-icarus.h
+
+// device detection
+
+static
+bool zeusminer_detect_one(const char *devpath)
+{
+	struct device_drv *drv = &zeusminer_drv;
+	
+	struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
+	if (unlikely(!info))
+		quit(1, "Failed to malloc ICARUS_INFO");
+	
+	char scrypt_golden_ob[] =
+	"55aa"																//Freq is set to 0x55*1.5=85Mhz
+	"0001"																//We want to find a Nonce which result's diff is at least 32768
+	"00038000"															//Starting Nonce
+	"063b0b1b"															//Bits (target in compact form)
+	"028f3253"															//Timestamp
+	"5e900609c15dc49a42b1d8492a6dd4f8f15295c989a1decf584a6aa93be26066"	//Merkle root
+	"d3185f55ef635b5865a7a79b7fa74121a6bb819da416328a9bd2f8cef72794bf"	//Previous hash
+	"02000000";															//Version
+	
+	const char scrypt_golden_nonce[] = "00038d26";
+	
+	*info = (struct ICARUS_INFO){
+		.baud = ZEUSMINER_IO_SPEED,
+		.timing_mode = MODE_DEFAULT,
+		.do_icarus_timing = true,
+		.probe_read_count = 5,
+		.golden_nonce = scrypt_golden_nonce,
+		.chips = ZEUSMINER_CHIPS_COUNT,
+		.freq = ZEUSMINER_DEFAULT_CLOCK,
+	};
+	
+	//pick up any user-defined settings passed in via --set
+	drv_set_defaults(drv, zeusminer_set_device_funcs, info, devpath, detectone_meta_info.serial, 1);
+	
+	info->work_division = upper_power_of_two_u32(info->chips * ZEUSMINER_CHIP_CORES);
+	info->fpga_count = info->chips * ZEUSMINER_CHIP_CORES;
+	
+	//send the requested Chip Speed with the detect golden OB
+	//we use the time this request takes in order to calc hashes
+	//so we need to use the same Chip Speed used when hashing
+	uint32_t clk_header = zeusminer_calc_clk_header(info->freq);
+	char clk_header_str[10];
+	sprintf(clk_header_str, "%08x", clk_header + 1);
+	memcpy(scrypt_golden_ob, clk_header_str, 8);
+	
+	info->golden_ob = scrypt_golden_ob;
+	
+	if (!icarus_detect_custom(devpath, drv, info))
+	{
+		free(info);
+		return false;
+	}
+	
+	double duration_sec;
+	const double hash_count = (double)0xd26;
+	uint64_t default_hashes_per_core = (((info->freq * 2) / 3) * 1024) / ZEUSMINER_CHIP_CORES;
+	
+	if (info->ignore_golden_nonce)
+		duration_sec = hash_count / default_hashes_per_core;
+	else
+		duration_sec = ((double)(info->golden_tv.tv_sec) + ((double)(info->golden_tv.tv_usec)) / ((double)1000000));
+	
+	//determines how the hash rate is calculated when no nonce is returned
+	info->Hs = (double)(duration_sec / hash_count / info->chips / ZEUSMINER_CHIP_CORES);
+	
+	//set the read_count (how long to wait for a result) based on chips, cores, and time to find a nonce
+	int chips_count_max = ZEUSMINER_CHIPS_COUNT_MAX;
+	if (info->chips > chips_count_max)
+		chips_count_max = upper_power_of_two_u32(info->chips);
+	//golden_speed_per_core is the number of hashes / second / core
+	uint64_t golden_speed_per_core = (uint64_t)(hash_count / duration_sec);
+	//don't combine the following two lines - overflows leaving info->read_count at 0
+	info->read_count = (uint32_t)((4294967296 * 10) / (ZEUSMINER_CHIP_CORES * chips_count_max * golden_speed_per_core * 2));
+	info->read_count = info->read_count * 3 / 4;
+	
+	return true;
+}
+
+// support for --set-device
+// must be set before probing the device
+
+static
+const char *zeusminer_set_clock(struct cgpu_info * const device, const char * const option, const char * const setting, char * const replybuf, enum bfg_set_device_replytype * const success)
+{
+	struct ICARUS_INFO * const info = device->device_data;
+	
+	int val = atoi(setting);
+	
+	if (val < ZEUSMINER_MIN_CLOCK || val > ZEUSMINER_MAX_CLOCK) {
+		sprintf(replybuf, "invalid clock: '%s' valid range %d-%d",
+		        setting, ZEUSMINER_MIN_CLOCK, ZEUSMINER_MAX_CLOCK);
+		return replybuf;
+	}
+	
+	info->freq = val;
+	
+	return NULL;
+}
+
+static
+const char *zeusminer_set_chips(struct cgpu_info * const device, const char * const option, const char * const setting, char * const replybuf, enum bfg_set_device_replytype * const success)
+{
+	struct ICARUS_INFO * const info = device->device_data;
+	
+	info->chips = atoi(setting);
+	
+	return NULL;
+}
+
+static
+const char *zeusminer_set_ignore_golden_nonce(struct cgpu_info * const device, const char * const option, const char * const setting, char * const replybuf, enum bfg_set_device_replytype * const success)
+{
+	struct ICARUS_INFO * const info = device->device_data;
+	
+	info->ignore_golden_nonce = atoi(setting) == 1;
+	
+	return NULL;
+}
+
+static
+const struct bfg_set_device_definition zeusminer_set_device_funcs[] = {
+	{ "clock", zeusminer_set_clock, NULL },
+	{ "chips", zeusminer_set_chips, NULL },
+	{ "ignore_golden_nonce",zeusminer_set_ignore_golden_nonce, NULL },
+	{ NULL },
+};
+
+static
+bool zeusminer_lowl_probe(const struct lowlevel_device_info * const info)
+{
+	return vcom_lowl_probe_wrapper(info, zeusminer_detect_one);
+}
+
+// device_drv functions - miner.h
+
+static
+bool zeusminer_thread_init(struct thr_info * const thr)
+{
+	struct cgpu_info * const device = thr->cgpu;
+	
+	device->min_nonce_diff = 1./0x10000;
+	
+	return icarus_init(thr);
+}
+
+static
+bool zeusminer_job_prepare(struct thr_info *thr, struct work *work, __maybe_unused uint64_t max_nonce)
+{
+	struct cgpu_info * const device = thr->cgpu;
+	struct icarus_state * const state = thr->cgpu_data;
+	struct ICARUS_INFO * const info = device->device_data;
+	
+	uint32_t clk_header = zeusminer_calc_clk_header(info->freq);
+	uint32_t diff = work->nonce_diff * 0x10000;
+	uint32_t target_me = 0xffff / diff;
+	uint32_t header = clk_header + target_me;
+	
+	pk_u32be(state->ob_bin, 0, header);
+	bswap_32mult(&state->ob_bin[4], work->data, 80/4);
+	
+	return true;
+}
+
+// device_drv definition - miner.h
+
+static
+void zeusminer_drv_init()
+{
+	// based on Icarus
+	zeusminer_drv = icarus_drv;
+	
+	// metadata
+	zeusminer_drv.dname = "zeusminer";
+	zeusminer_drv.name = "ZUS";
+	zeusminer_drv.supported_algos = POW_SCRYPT;
+	
+	// detect device
+	zeusminer_drv.lowl_probe = zeusminer_lowl_probe;
+	
+	// initialize thread
+	zeusminer_drv.thread_init = zeusminer_thread_init;
+	
+	// Icarus scanhash mining hooks
+	zeusminer_drv.job_prepare = zeusminer_job_prepare;
+	
+	// specify driver probe priority
+	++zeusminer_drv.probe_priority;
+}
+
+struct device_drv zeusminer_drv = {
+	.drv_init = zeusminer_drv_init,
+};

+ 12 - 0
miner.h

@@ -745,6 +745,18 @@ void bswap_96p(void * const dest_p, const void * const src_p)
 	dest[2] = bswap_32(src[0]);
 }
 
+static inline
+void bswap_32mult(void * const dest_p, const void * const src_p, const size_t sz)
+{
+	const uint32_t *s = src_p;
+	const uint32_t *s_end = &s[sz];
+	uint32_t *d = dest_p;
+	d = &d[sz - 1];
+	
+	for ( ; s < s_end; ++s, --d)
+		*d = bswap_32(*s);
+}
+
 #define flip32(dest_p, src_p) swap32yes(dest_p, src_p, 32 / 4)
 
 #define WATCHDOG_INTERVAL  2

+ 3 - 0
util.h

@@ -407,6 +407,9 @@ void pk_u64le(void * const bufp, const int offset, const uint64_t nv)
 	buf[offset+7] = (nv >> 0x38) & 0xff;
 }
 
+#define is_power_of_two(n)  \
+	(0 == ((n) && ((n) - 1)))
+
 static inline
 uint32_t upper_power_of_two_u32(uint32_t n)
 {