Browse Source

First draft of port of avalon driver to new cgminer queued infrastructure.

Con Kolivas 13 years ago
parent
commit
71bae003bc
7 changed files with 1342 additions and 6 deletions
  1. 32 2
      cgminer.c
  2. 1060 0
      driver-avalon.c
  3. 136 0
      driver-avalon.h
  4. 22 0
      fpgautils.c
  5. 2 0
      fpgautils.h
  6. 77 0
      hexdump.c
  7. 13 4
      miner.h

+ 32 - 2
cgminer.c

@@ -48,6 +48,7 @@
 #include "driver-opencl.h"
 #include "bench_block.h"
 #include "scrypt.h"
+#include "driver-avalon.h"
 
 #if defined(unix)
 	#include <errno.h>
@@ -55,9 +56,9 @@
 	#include <sys/wait.h>
 #endif
 
-#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER)
+#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_MODMINER)
 #	define USE_FPGA
-#if defined(USE_ICARUS)
+#if defined(USE_ICARUS) || defined(USE_AVALON)
 #	define USE_FPGA_SERIAL
 #endif
 #elif defined(USE_ZTEX)
@@ -138,6 +139,9 @@ bool opt_disable_pool;
 char *opt_icarus_options = NULL;
 char *opt_icarus_timing = NULL;
 bool opt_worktime;
+#ifdef USE_AVALON
+char *opt_avalon_options = NULL;
+#endif
 #ifdef USE_USBUTILS
 char *opt_usb_select = NULL;
 int opt_usbdump = -1;
@@ -833,6 +837,15 @@ static char *set_icarus_timing(const char *arg)
 }
 #endif
 
+#ifdef USE_AVALON
+static char *set_avalon_options(const char *arg)
+{
+	opt_set_charp(arg, &opt_avalon_options);
+
+	return NULL;
+}
+#endif
+
 #ifdef USE_USBUTILS
 static char *set_usb_select(const char *arg)
 {
@@ -1032,6 +1045,11 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--icarus-timing",
 		     set_icarus_timing, NULL, NULL,
 		     opt_hidden),
+#endif
+#ifdef USE_AVALON
+	OPT_WITH_ARG("--avalon-options",
+		     set_avalon_options, NULL, NULL,
+		     opt_hidden),
 #endif
 	OPT_WITHOUT_ARG("--load-balance",
 		     set_loadbalance, &pool_strategy,
@@ -1359,6 +1377,9 @@ static char *opt_verusage_and_exit(const char *extra)
 #ifdef USE_ICARUS
 		"icarus "
 #endif
+#ifdef USE_AVALON
+		"avalon "
+#endif
 #ifdef USE_MODMINER
 		"modminer "
 #endif
@@ -6792,6 +6813,10 @@ extern struct device_drv bitforce_drv;
 extern struct device_drv icarus_drv;
 #endif
 
+#ifdef USE_AVALON
+extern struct device_drv avalon_api;
+#endif
+
 #ifdef USE_MODMINER
 extern struct device_drv modminer_drv;
 #endif
@@ -7311,6 +7336,11 @@ int main(int argc, char *argv[])
 		icarus_drv.drv_detect();
 #endif
 
+#ifdef USE_AVALON
+	if (!opt_scrypt)
+		avalon_api.drv_detect();
+#endif
+
 #ifdef USE_BFLSC
 	if (!opt_scrypt)
 		bflsc_drv.drv_detect();

+ 1060 - 0
driver-avalon.c

@@ -0,0 +1,1060 @@
+/*
+ * Copyright 2013 Con Kolivas <kernel@kolivas.org>
+ * Copyright 2012-2013 Xiangfu <xiangfu@openmobilefree.com>
+ * Copyright 2012 Luke Dashjr
+ * Copyright 2012 Andrew Smith
+ *
+ * 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 <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <dirent.h>
+#include <unistd.h>
+#ifndef WIN32
+  #include <termios.h>
+  #include <sys/stat.h>
+  #include <fcntl.h>
+  #ifndef O_CLOEXEC
+    #define O_CLOEXEC 0
+  #endif
+#else
+  #include <windows.h>
+  #include <io.h>
+#endif
+
+#include "elist.h"
+#include "miner.h"
+#include "fpgautils.h"
+#include "driver-avalon.h"
+#include "hexdump.c"
+
+static int option_offset = -1;
+struct avalon_info **avalon_info;
+struct device_drv avalon_api;
+
+static int avalon_init_task(struct avalon_task *at,
+			    uint8_t reset, uint8_t ff, uint8_t fan,
+			    uint8_t timeout, uint8_t asic_num,
+			    uint8_t miner_num, uint8_t nonce_elf,
+			    uint8_t gate_miner, int frequency)
+{
+	uint8_t *buf;
+	static bool first = true;
+
+	if (unlikely(!at))
+		return -1;
+
+	if (unlikely(timeout <= 0 || asic_num <= 0 || miner_num <= 0))
+		return -1;
+
+	memset(at, 0, sizeof(struct avalon_task));
+
+	if (unlikely(reset)) {
+		at->reset = 1;
+		at->fan_eft = 1;
+		at->timer_eft = 1;
+		first = true;
+	}
+
+	at->flush_fifo = (ff ? 1 : 0);
+	at->fan_eft = (fan ? 1 : 0);
+
+	if (unlikely(first && !at->reset)) {
+		at->fan_eft = 1;
+		at->timer_eft = 1;
+		first = false;
+	}
+
+	at->fan_pwm_data = (fan ? fan : AVALON_DEFAULT_FAN_MAX_PWM);
+	at->timeout_data = timeout;
+	at->asic_num = asic_num;
+	at->miner_num = miner_num;
+	at->nonce_elf = nonce_elf;
+
+	at->gate_miner_elf = 1;
+	at->asic_pll = 1;
+
+	if (unlikely(gate_miner)) {
+		at-> gate_miner = 1;
+		at->asic_pll = 0;
+	}
+
+	buf = (uint8_t *)at;
+	buf[5] = 0x00;
+	buf[8] = 0x74;
+	buf[9] = 0x01;
+	buf[10] = 0x00;
+	buf[11] = 0x00;
+	if (frequency == 256) {
+		buf[6] = 0x03;
+		buf[7] = 0x08;
+	} else if (frequency == 270) {
+		buf[6] = 0x73;
+		buf[7] = 0x08;
+	} else if (frequency == 282) {
+		buf[6] = 0xd3;
+		buf[7] = 0x08;
+	} else if (frequency == 300) {
+		buf[6] = 0x63;
+		buf[7] = 0x09;
+	}
+
+	return 0;
+}
+
+static inline void avalon_create_task(struct avalon_task *at,
+				      struct work *work)
+{
+	memcpy(at->midstate, work->midstate, 32);
+	memcpy(at->data, work->data + 64, 12);
+}
+
+static int avalon_send_task(int fd, const struct avalon_task *at,
+			    struct cgpu_info *avalon)
+
+{
+	size_t ret;
+	int full;
+	struct timespec p;
+	uint8_t buf[AVALON_WRITE_SIZE + 4 * AVALON_DEFAULT_ASIC_NUM];
+	size_t nr_len;
+	struct avalon_info *info;
+	uint64_t delay = 32000000; /* Default 32ms for B19200 */
+	uint32_t nonce_range;
+	int i;
+
+	if (at->nonce_elf)
+		nr_len = AVALON_WRITE_SIZE + 4 * at->asic_num;
+	else
+		nr_len = AVALON_WRITE_SIZE;
+
+	memcpy(buf, at, AVALON_WRITE_SIZE);
+
+	if (at->nonce_elf) {
+		nonce_range = (uint32_t)0xffffffff / at->asic_num;
+		for (i = 0; i < at->asic_num; i++) {
+			buf[AVALON_WRITE_SIZE + (i * 4) + 3] =
+				(i * nonce_range & 0xff000000) >> 24;
+			buf[AVALON_WRITE_SIZE + (i * 4) + 2] =
+				(i * nonce_range & 0x00ff0000) >> 16;
+			buf[AVALON_WRITE_SIZE + (i * 4) + 1] =
+				(i * nonce_range & 0x0000ff00) >> 8;
+			buf[AVALON_WRITE_SIZE + (i * 4) + 0] =
+				(i * nonce_range & 0x000000ff) >> 0;
+		}
+	}
+#if defined(__BIG_ENDIAN__) || defined(MIPSEB)
+	uint8_t tt = 0;
+
+	tt = (buf[0] & 0x0f) << 4;
+	tt |= ((buf[0] & 0x10) ? (1 << 3) : 0);
+	tt |= ((buf[0] & 0x20) ? (1 << 2) : 0);
+	tt |= ((buf[0] & 0x40) ? (1 << 1) : 0);
+	tt |= ((buf[0] & 0x80) ? (1 << 0) : 0);
+	buf[0] = tt;
+
+	tt = (buf[4] & 0x0f) << 4;
+	tt |= ((buf[4] & 0x10) ? (1 << 3) : 0);
+	tt |= ((buf[4] & 0x20) ? (1 << 2) : 0);
+	tt |= ((buf[4] & 0x40) ? (1 << 1) : 0);
+	tt |= ((buf[4] & 0x80) ? (1 << 0) : 0);
+	buf[4] = tt;
+#endif
+	if (likely(avalon)) {
+		info = avalon_info[avalon->device_id];
+		delay = nr_len * 10 * 1000000000ULL;
+		delay = delay / info->baud;
+	}
+
+	if (at->reset)
+		nr_len = 1;
+	if (opt_debug) {
+		applog(LOG_DEBUG, "Avalon: Sent(%d):", nr_len);
+		hexdump((uint8_t *)buf, nr_len);
+	}
+	ret = write(fd, buf, nr_len);
+	if (unlikely(ret != nr_len))
+		return AVA_SEND_ERROR;
+
+	p.tv_sec = 0;
+	p.tv_nsec = (long)delay + 4000000;
+	nanosleep(&p, NULL);
+	applog(LOG_DEBUG, "Avalon: Sent: Buffer delay: %ld", p.tv_nsec);
+
+	full = avalon_buffer_full(fd);
+	applog(LOG_DEBUG, "Avalon: Sent: Buffer full: %s",
+	       ((full == AVA_BUFFER_FULL) ? "Yes" : "No"));
+
+	if (unlikely(full == AVA_BUFFER_FULL))
+		return AVA_SEND_BUFFER_FULL;
+
+	return AVA_SEND_BUFFER_EMPTY;
+}
+
+static int avalon_gets(int fd, uint8_t *buf, int read_count,
+		       struct thr_info *thr, struct timeval *tv_finish)
+{
+	ssize_t ret = 0;
+	int rc = 0;
+	int read_amount = AVALON_READ_SIZE;
+	bool first = true;
+
+	while (true) {
+		struct timeval timeout = {0, 100000};
+		fd_set rd;
+
+		FD_ZERO(&rd);
+		FD_SET(fd, &rd);
+		ret = select(fd + 1, &rd, NULL, NULL, &timeout);
+		if (unlikely(ret < 0))
+			return AVA_GETS_ERROR;
+		if (ret) {
+			ret = read(fd, buf, read_amount);
+			if (unlikely(ret < 0))
+				return AVA_GETS_ERROR;
+			if (likely(first)) {
+				if (likely(tv_finish))
+					gettimeofday(tv_finish, NULL);
+				first = false;
+			}
+			if (likely(ret >= read_amount))
+				return AVA_GETS_OK;
+			buf += ret;
+			read_amount -= ret;
+			continue;
+		}
+
+		rc++;
+		if (rc >= read_count) {
+			if (opt_debug) {
+				applog(LOG_WARNING,
+				       "Avalon: No data in %.2f seconds",
+				       (float)rc/(float)AVALON_TIME_FACTOR);
+			}
+			return AVA_GETS_TIMEOUT;
+		}
+
+		if (thr && thr->work_restart) {
+			if (opt_debug) {
+				applog(LOG_WARNING,
+				       "Avalon: Work restart at %.2f seconds",
+				       (float)(rc)/(float)AVALON_TIME_FACTOR);
+			}
+			return AVA_GETS_RESTART;
+		}
+	}
+}
+
+static int avalon_get_result(int fd, struct avalon_result *ar,
+			     struct thr_info *thr, struct timeval *tv_finish)
+{
+	struct cgpu_info *avalon;
+	struct avalon_info *info;
+	uint8_t result[AVALON_READ_SIZE];
+	int ret, read_count = AVALON_RESET_FAULT_DECISECONDS * AVALON_TIME_FACTOR;
+
+	if (likely(thr)) {
+		avalon = thr->cgpu;
+		info = avalon_info[avalon->device_id];
+		read_count = info->read_count;
+	}
+
+	memset(result, 0, AVALON_READ_SIZE);
+	ret = avalon_gets(fd, result, read_count, thr, tv_finish);
+
+	if (ret == AVA_GETS_OK) {
+		if (opt_debug) {
+			applog(LOG_DEBUG, "Avalon: get:");
+			hexdump((uint8_t *)result, AVALON_READ_SIZE);
+		}
+		memcpy((uint8_t *)ar, result, AVALON_READ_SIZE);
+	}
+
+	return ret;
+}
+
+static int avalon_decode_nonce(struct thr_info *thr, struct work **work,
+			       struct avalon_result *ar, uint32_t *nonce)
+{
+	struct cgpu_info *avalon;
+	struct avalon_info *info;
+	int avalon_get_work_count, i;
+
+	if (unlikely(!work))
+		return -1;
+
+	avalon = thr->cgpu;
+	info = avalon_info[avalon->device_id];
+	avalon_get_work_count = info->miner_count;
+
+	for (i = 0; i < avalon_get_work_count; i++) {
+		if (work[i] &&
+		    !memcmp(ar->data, work[i]->data + 64, 12) &&
+		    !memcmp(ar->midstate, work[i]->midstate, 32))
+			break;
+	}
+	if (i == avalon_get_work_count)
+		return -1;
+
+	info->matching_work[i]++;
+	*nonce = htole32(ar->nonce);
+
+	applog(LOG_DEBUG, "Avalon: match to work[%d](%p): %d",i, work[i],
+	       info->matching_work[i]);
+	return i;
+}
+
+static int avalon_reset(int fd, struct avalon_result *ar)
+{
+	struct avalon_task at;
+	uint8_t *buf;
+	int ret, i = 0;
+	struct timespec p;
+
+	avalon_init_task(&at, 1, 0,
+			 AVALON_DEFAULT_FAN_MAX_PWM,
+			 AVALON_DEFAULT_TIMEOUT,
+			 AVALON_DEFAULT_ASIC_NUM,
+			 AVALON_DEFAULT_MINER_NUM,
+			 0, 0,
+			 AVALON_DEFAULT_FREQUENCY);
+	ret = avalon_send_task(fd, &at, NULL);
+	if (ret == AVA_SEND_ERROR)
+		return 1;
+
+	avalon_get_result(fd, ar, NULL, NULL);
+
+	buf = (uint8_t *)ar;
+	/* Sometimes there is one extra 0 byte for some reason in the buffer,
+	 * so work around it. */
+	if (buf[0] == 0)
+		buf = (uint8_t  *)(ar + 1);
+	if (buf[0] == 0xAA && buf[1] == 0x55 &&
+	    buf[2] == 0xAA && buf[3] == 0x55) {
+		for (i = 4; i < 11; i++)
+			if (buf[i] != 0)
+				break;
+	}
+
+	p.tv_sec = 0;
+	p.tv_nsec = AVALON_RESET_PITCH;
+	nanosleep(&p, NULL);
+
+	if (i != 11) {
+		applog(LOG_ERR, "Avalon: Reset failed! not an Avalon?"
+		       " (%d: %02x %02x %02x %02x)",
+		       i, buf[0], buf[1], buf[2], buf[3]);
+		/* FIXME: return 1; */
+	} else
+		applog(LOG_WARNING, "Avalon: Reset succeeded");
+	return 0;
+}
+
+static void avalon_idle(struct cgpu_info *avalon)
+{
+	int i, ret;
+	struct avalon_task at;
+
+	int fd = avalon->device_fd;
+	struct avalon_info *info = avalon_info[avalon->device_id];
+	int avalon_get_work_count = info->miner_count;
+
+	i = 0;
+	while (true) {
+		avalon_init_task(&at, 0, 0, info->fan_pwm,
+				 info->timeout, info->asic_count,
+				 info->miner_count, 1, 1, info->frequency);
+		ret = avalon_send_task(fd, &at, avalon);
+		if (unlikely(ret == AVA_SEND_ERROR ||
+			     (ret == AVA_SEND_BUFFER_EMPTY &&
+			      (i + 1 == avalon_get_work_count * 2)))) {
+			applog(LOG_ERR, "AVA%i: Comms error", avalon->device_id);
+			return;
+		}
+		if (i + 1 == avalon_get_work_count * 2)
+			break;
+
+		if (ret == AVA_SEND_BUFFER_FULL)
+			break;
+
+		i++;
+	}
+	applog(LOG_ERR, "Avalon: Goto idle mode");
+}
+
+static void get_options(int this_option_offset, int *baud, int *miner_count,
+			int *asic_count, int *timeout, int *frequency)
+{
+	char err_buf[BUFSIZ+1];
+	char buf[BUFSIZ+1];
+	char *ptr, *comma, *colon, *colon2, *colon3, *colon4;
+	size_t max;
+	int i, tmp;
+
+	if (opt_avalon_options == NULL)
+		buf[0] = '\0';
+	else {
+		ptr = opt_avalon_options;
+		for (i = 0; i < this_option_offset; i++) {
+			comma = strchr(ptr, ',');
+			if (comma == NULL)
+				break;
+			ptr = comma + 1;
+		}
+
+		comma = strchr(ptr, ',');
+		if (comma == NULL)
+			max = strlen(ptr);
+		else
+			max = comma - ptr;
+
+		if (max > BUFSIZ)
+			max = BUFSIZ;
+		strncpy(buf, ptr, max);
+		buf[max] = '\0';
+	}
+
+	*baud = AVALON_IO_SPEED;
+	*miner_count = AVALON_DEFAULT_MINER_NUM - 8;
+	*asic_count = AVALON_DEFAULT_ASIC_NUM;
+	*timeout = AVALON_DEFAULT_TIMEOUT;
+	*frequency = AVALON_DEFAULT_FREQUENCY;
+
+	if (!(*buf))
+		return;
+
+	colon = strchr(buf, ':');
+	if (colon)
+		*(colon++) = '\0';
+
+	tmp = atoi(buf);
+	switch (tmp) {
+	case 115200:
+		*baud = 115200;
+		break;
+	case 57600:
+		*baud = 57600;
+		break;
+	case 38400:
+		*baud = 38400;
+		break;
+	case 19200:
+		*baud = 19200;
+		break;
+	default:
+		sprintf(err_buf,
+			"Invalid avalon-options for baud (%s) "
+			"must be 115200, 57600, 38400 or 19200", buf);
+		quit(1, err_buf);
+	}
+
+	if (colon && *colon) {
+		colon2 = strchr(colon, ':');
+		if (colon2)
+			*(colon2++) = '\0';
+
+		if (*colon) {
+			tmp = atoi(colon);
+			if (tmp > 0 && tmp <= AVALON_DEFAULT_MINER_NUM) {
+				*miner_count = tmp;
+			} else {
+				sprintf(err_buf,
+					"Invalid avalon-options for "
+					"miner_count (%s) must be 1 ~ %d",
+					colon, AVALON_DEFAULT_MINER_NUM);
+				quit(1, err_buf);
+			}
+		}
+
+		if (colon2 && *colon2) {
+			colon3 = strchr(colon2, ':');
+			if (colon3)
+				*(colon3++) = '\0';
+
+			tmp = atoi(colon2);
+			if (tmp > 0 && tmp <= AVALON_DEFAULT_ASIC_NUM)
+				*asic_count = tmp;
+			else {
+				sprintf(err_buf,
+					"Invalid avalon-options for "
+					"asic_count (%s) must be 1 ~ %d",
+					colon2, AVALON_DEFAULT_ASIC_NUM);
+				quit(1, err_buf);
+			}
+
+			if (colon3 && *colon3) {
+				colon4 = strchr(colon3, ':');
+				if (colon4)
+					*(colon4++) = '\0';
+
+				tmp = atoi(colon3);
+				if (tmp > 0 && tmp <= 0xff)
+					*timeout = tmp;
+				else {
+					sprintf(err_buf,
+						"Invalid avalon-options for "
+						"timeout (%s) must be 1 ~ %d",
+						colon3, 0xff);
+					quit(1, err_buf);
+				}
+				if (colon4 && *colon4) {
+					tmp = atoi(colon4);
+					switch (tmp) {
+					case 256:
+					case 270:
+					case 282:
+					case 300:
+						*frequency = tmp;
+						break;
+					default:
+						sprintf(err_buf,
+							"Invalid avalon-options for "
+							"frequency must be 256/270/282/300");
+							quit(1, err_buf);
+					}
+				}
+			}
+		}
+	}
+}
+
+static bool avalon_detect_one(const char *devpath)
+{
+	struct avalon_info *info;
+	struct avalon_result ar;
+	int fd, ret;
+	int baud, miner_count, asic_count, timeout, frequency = 0;
+	struct cgpu_info *avalon;
+
+	int this_option_offset = ++option_offset;
+	get_options(this_option_offset, &baud, &miner_count, &asic_count,
+		    &timeout, &frequency);
+
+	applog(LOG_DEBUG, "Avalon Detect: Attempting to open %s "
+	       "(baud=%d miner_count=%d asic_count=%d timeout=%d frequency=%d)",
+	       devpath, baud, miner_count, asic_count, timeout, frequency);
+
+	fd = avalon_open2(devpath, baud, true);
+	if (unlikely(fd == -1)) {
+		applog(LOG_ERR, "Avalon Detect: Failed to open %s", devpath);
+		return false;
+	}
+
+	/* We have a real Avalon! */
+	avalon = calloc(1, sizeof(struct cgpu_info));
+	avalon->drv = &avalon_api;
+	avalon->device_path = strdup(devpath);
+	avalon->device_fd = fd;
+	avalon->threads = AVALON_MINER_THREADS;
+	add_cgpu(avalon);
+
+	ret = avalon_reset(fd, &ar);
+	if (ret) {
+		; /* FIXME: I think IT IS avalon and wait on reset;
+		   * avalon_close(fd);
+		   * return false; */
+	}
+	
+	avalon_info = realloc(avalon_info,
+			      sizeof(struct avalon_info *) *
+			      (total_devices + 1));
+
+	applog(LOG_INFO, "Avalon Detect: Found at %s, mark as %d",
+	       devpath, avalon->device_id);
+
+	avalon_info[avalon->device_id] = (struct avalon_info *)
+		malloc(sizeof(struct avalon_info));
+	if (unlikely(!(avalon_info[avalon->device_id])))
+		quit(1, "Failed to malloc avalon_info");
+
+	info = avalon_info[avalon->device_id];
+
+	memset(info, 0, sizeof(struct avalon_info));
+
+	info->baud = baud;
+	info->miner_count = miner_count;
+	info->asic_count = asic_count;
+	info->timeout = timeout;
+	info->read_count = ((float)info->timeout * AVALON_HASH_TIME_FACTOR *
+			    AVALON_TIME_FACTOR) / (float)info->miner_count;
+
+	info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM;
+	info->temp_max = 0;
+	/* This is for check the temp/fan every 3~4s */
+	info->temp_history_count = (4 / (float)((float)info->timeout * ((float)1.67/0x32))) + 1;
+	if (info->temp_history_count <= 0)
+		info->temp_history_count = 1;
+
+	info->temp_history_index = 0;
+	info->temp_sum = 0;
+	info->temp_old = 0;
+	info->frequency = frequency;
+
+	/* Do something for failed reset ? */
+	if (0) {
+		/* Set asic to idle mode after detect */
+		avalon_idle(avalon);
+		avalon->device_fd = -1;
+
+		avalon_close(fd);
+	}
+	return true;
+}
+
+static inline void avalon_detect()
+{
+	serial_detect(&avalon_api, avalon_detect_one);
+}
+
+static void __avalon_init(struct cgpu_info *avalon)
+{
+	applog(LOG_INFO, "Avalon: Opened on %s", avalon->device_path);
+}
+
+static void avalon_init(struct cgpu_info *avalon)
+{
+	struct avalon_result ar;
+	int fd, ret;
+
+	avalon->device_fd = -1;
+	fd = avalon_open(avalon->device_path,
+			     avalon_info[avalon->device_id]->baud);
+	if (unlikely(fd == -1)) {
+		applog(LOG_ERR, "Avalon: Failed to open on %s",
+		       avalon->device_path);
+		return;
+	}
+
+	ret = avalon_reset(fd, &ar);
+	if (ret) {
+		avalon_close(fd);
+		return;
+	}
+
+	avalon->device_fd = fd;
+	__avalon_init(avalon);
+}
+
+static bool avalon_prepare(struct thr_info *thr)
+{
+	struct cgpu_info *avalon = thr->cgpu;
+	struct avalon_info *info = avalon_info[avalon->device_id];
+	struct timeval now;
+
+	avalon->works = calloc(info->miner_count * sizeof(struct work *), 1);
+	if (!avalon->works)
+		quit(1, "Failed to calloc avalon works in avalon_prepare");
+	__avalon_init(avalon);
+
+	gettimeofday(&now, NULL);
+	get_datestamp(avalon->init, &now);
+	return true;
+}
+
+static void avalon_free_work(struct thr_info *thr, struct work **works)
+{
+	struct cgpu_info *avalon;
+	struct avalon_info *info;
+	int i;
+
+	if (unlikely(!works))
+		return;
+
+	avalon = thr->cgpu;
+	info = avalon_info[avalon->device_id];
+
+	for (i = 0; i < info->miner_count; i++) {
+		if (likely(works[i])) {
+			work_completed(avalon, works[i]);
+			works[i] = NULL;
+		}
+	}
+}
+
+static void do_avalon_close(struct thr_info *thr)
+{
+	struct avalon_result ar;
+	struct cgpu_info *avalon = thr->cgpu;
+	struct avalon_info *info = avalon_info[avalon->device_id];
+
+	sleep(1);
+	avalon_reset(avalon->device_fd, &ar);
+	avalon_idle(avalon);
+	avalon_close(avalon->device_fd);
+	avalon->device_fd = -1;
+
+	info->no_matching_work = 0;
+	avalon_free_work(thr, info->bulk0);
+	avalon_free_work(thr, info->bulk1);
+	avalon_free_work(thr, info->bulk2);
+	avalon_free_work(thr, info->bulk3);
+}
+
+static inline void record_temp_fan(struct avalon_info *info, struct avalon_result *ar, float *temp_avg)
+{
+	int max;
+
+	info->fan0 = ar->fan0 * AVALON_FAN_FACTOR;
+	info->fan1 = ar->fan1 * AVALON_FAN_FACTOR;
+	info->fan2 = ar->fan2 * AVALON_FAN_FACTOR;
+
+	info->temp0 = ar->temp0;
+	info->temp1 = ar->temp1;
+	info->temp2 = ar->temp2;
+	if (ar->temp0 & 0x80) {
+		ar->temp0 &= 0x7f;
+		info->temp0 = 0 - ((~ar->temp0 & 0x7f) + 1);
+	}
+	if (ar->temp1 & 0x80) {
+		ar->temp1 &= 0x7f;
+		info->temp1 = 0 - ((~ar->temp1 & 0x7f) + 1);
+	}
+	if (ar->temp2 & 0x80) {
+		ar->temp2 &= 0x7f;
+		info->temp2 = 0 - ((~ar->temp2 & 0x7f) + 1);
+	}
+
+	*temp_avg = info->temp2;
+
+	max = info->temp_max;
+	if (info->temp0 > max)
+		max = info->temp0;
+	if (info->temp1 > max)
+		max = info->temp1;
+	if (info->temp2 > max)
+		max = info->temp2;
+	if (max >= 100) {	/* FIXME: fix the root cause on fpga controller firmware */
+		if (opt_debug) {
+			applog(LOG_DEBUG, "Avalon: temp_max: %d", max);
+			hexdump((uint8_t *)ar, AVALON_READ_SIZE);
+		}
+		return;
+	}
+
+	info->temp_max = max;
+}
+
+static inline void adjust_fan(struct avalon_info *info)
+{
+	int temp_new;
+
+	temp_new = info->temp_sum / info->temp_history_count;
+
+	if (temp_new < 35) {
+		info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM;
+		info->temp_old = temp_new;
+	} else if (temp_new > 55) {
+		info->fan_pwm = AVALON_DEFAULT_FAN_MAX_PWM;
+		info->temp_old = temp_new;
+	} else if (abs(temp_new - info->temp_old) >= 2) {
+		info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM + (temp_new - 35) * 6.4;
+		info->temp_old = temp_new;
+	}
+}
+
+static bool avalon_fill(struct cgpu_info *avalon)
+{
+	struct work *work = get_queued(avalon);
+
+	if (unlikely(!work))
+		return false;
+	avalon->queued++;
+	if (avalon->queued == avalon_info[avalon->device_id]->miner_count)
+		return true;
+	return false;
+}
+
+static int64_t avalon_scanhash(struct thr_info *thr)
+{
+	struct cgpu_info *avalon;
+	struct work **works;
+	int fd, ret, full;
+	int64_t scanret = 0;
+
+	struct avalon_info *info;
+	struct avalon_task at;
+	struct avalon_result ar;
+	int i, work_i0, work_i1, work_i2, work_i3;
+	int avalon_get_work_count;
+
+	struct timeval tv_start, tv_finish, elapsed;
+	uint32_t nonce;
+	int64_t hash_count;
+	static int first_try = 0;
+	int result_count, result_wrong;
+
+	avalon = thr->cgpu;
+	works = avalon->works;
+	info = avalon_info[avalon->device_id];
+	avalon_get_work_count = info->miner_count;
+
+	if (unlikely(avalon->device_fd == -1)) {
+		if (!avalon_prepare(thr)) {
+			applog(LOG_ERR, "AVA%i: Comms error(open)",
+			       avalon->device_id);
+			dev_error(avalon, REASON_DEV_COMMS_ERROR);
+			/* fail the device if the reopen attempt fails */
+			scanret = -1;
+			goto out;
+		}
+	}
+	fd = avalon->device_fd;
+#ifndef WIN32
+	tcflush(fd, TCOFLUSH);
+#endif
+
+	for (i = 0; i < avalon_get_work_count; i++) {
+		info->bulk0[i] = info->bulk1[i];
+		info->bulk1[i] = info->bulk2[i];
+		info->bulk2[i] = info->bulk3[i];
+		info->bulk3[i] = works[i];
+		applog(LOG_DEBUG, "Avalon: bulk0/1/2 buffer [%d]: %p, %p, %p, %p",
+		       i, info->bulk0[i], info->bulk1[i], info->bulk2[i], info->bulk3[i]);
+	}
+
+	i = 0;
+	while (true) {
+		avalon_init_task(&at, 0, 0, info->fan_pwm,
+				 info->timeout, info->asic_count,
+				 info->miner_count, 1, 0, info->frequency);
+		avalon_create_task(&at, works[i]);
+		ret = avalon_send_task(fd, &at, avalon);
+		if (unlikely(ret == AVA_SEND_ERROR ||
+			     (ret == AVA_SEND_BUFFER_EMPTY &&
+			      (i + 1 == avalon_get_work_count) &&
+			      first_try))) {
+			avalon_free_work(thr, info->bulk0);
+			avalon_free_work(thr, info->bulk1);
+			avalon_free_work(thr, info->bulk2);
+			avalon_free_work(thr, info->bulk3);
+			do_avalon_close(thr);
+			applog(LOG_ERR, "AVA%i: Comms error(buffer)",
+			       avalon->device_id);
+			dev_error(avalon, REASON_DEV_COMMS_ERROR);
+			first_try = 0;
+			sleep(1);
+			avalon_init(avalon);
+			goto out;	/* This should never happen */
+		}
+		if (ret == AVA_SEND_BUFFER_EMPTY && (i + 1 == avalon_get_work_count)) {
+			first_try = 1;
+			ret = 0xffffffff;
+			goto out;
+		}
+
+		works[i]->blk.nonce = 0xffffffff;
+
+		if (ret == AVA_SEND_BUFFER_FULL)
+			break;
+
+		i++;
+	}
+	if (unlikely(first_try))
+		first_try = 0;
+
+	elapsed.tv_sec = elapsed.tv_usec = 0;
+	gettimeofday(&tv_start, NULL);
+
+	result_count = 0;
+	result_wrong = 0;
+	hash_count = 0;
+	while (true) {
+		work_i0 = work_i1 = work_i2 = work_i3 = -1;
+
+		full = avalon_buffer_full(fd);
+		applog(LOG_DEBUG, "Avalon: Buffer full: %s",
+		       ((full == AVA_BUFFER_FULL) ? "Yes" : "No"));
+		if (unlikely(full == AVA_BUFFER_EMPTY))
+			break;
+
+		ret = avalon_get_result(fd, &ar, thr, &tv_finish);
+		if (unlikely(ret == AVA_GETS_ERROR)) {
+			avalon_free_work(thr, info->bulk0);
+			avalon_free_work(thr, info->bulk1);
+			avalon_free_work(thr, info->bulk2);
+			avalon_free_work(thr, info->bulk3);
+			do_avalon_close(thr);
+			applog(LOG_ERR,
+			       "AVA%i: Comms error(read)", avalon->device_id);
+			dev_error(avalon, REASON_DEV_COMMS_ERROR);
+			goto out;
+		}
+		if (unlikely(ret == AVA_GETS_TIMEOUT)) {
+			timersub(&tv_finish, &tv_start, &elapsed);
+			applog(LOG_DEBUG, "Avalon: no nonce in (%ld.%06lds)",
+			       elapsed.tv_sec, elapsed.tv_usec);
+			continue;
+		}
+		if (unlikely(ret == AVA_GETS_RESTART)) {
+			avalon_free_work(thr, info->bulk0);
+			avalon_free_work(thr, info->bulk1);
+			avalon_free_work(thr, info->bulk2);
+			avalon_free_work(thr, info->bulk3);
+			break;
+		}
+		result_count++;
+
+		work_i0 = avalon_decode_nonce(thr, info->bulk0, &ar, &nonce);
+		if (work_i0 < 0) {
+			work_i1 = avalon_decode_nonce(thr, info->bulk1, &ar, &nonce);
+			if (work_i1 < 0) {
+				work_i2 = avalon_decode_nonce(thr, info->bulk2, &ar, &nonce);
+				if (work_i2 < 0) {
+					work_i3 = avalon_decode_nonce(thr, info->bulk3, &ar, &nonce);
+					if (work_i3 < 0) {
+						info->no_matching_work++;
+						result_wrong++;
+
+						if (opt_debug) {
+							timersub(&tv_finish, &tv_start, &elapsed);
+							applog(LOG_DEBUG,"Avalon: no matching work: %d"
+							" (%ld.%06lds)", info->no_matching_work,
+							elapsed.tv_sec, elapsed.tv_usec);
+						}
+						continue;
+					} else
+						submit_nonce(thr, info->bulk3[work_i3], nonce);
+				} else
+					submit_nonce(thr, info->bulk2[work_i2], nonce);
+			} else
+				submit_nonce(thr, info->bulk1[work_i1], nonce);
+		} else
+			submit_nonce(thr, info->bulk0[work_i0], nonce);
+
+		hash_count += nonce;
+		if (opt_debug) {
+			timersub(&tv_finish, &tv_start, &elapsed);
+			applog(LOG_DEBUG,
+			       "Avalon: nonce = 0x%08x = 0x%08llx hashes "
+			       "(%ld.%06lds)", nonce, hash_count,
+			       elapsed.tv_sec, elapsed.tv_usec);
+		}
+	}
+	if (result_wrong && result_count == result_wrong) {
+		/* This mean FPGA controller give all wrong result
+		 * try to reset the Avalon */
+		avalon_free_work(thr, info->bulk0);
+		avalon_free_work(thr, info->bulk1);
+		avalon_free_work(thr, info->bulk2);
+		avalon_free_work(thr, info->bulk3);
+		do_avalon_close(thr);
+		applog(LOG_ERR,
+		       "AVA%i: FPGA controller mess up", avalon->device_id);
+		dev_error(avalon, REASON_DEV_COMMS_ERROR);
+		do_avalon_close(thr);
+		sleep(1);
+		avalon_init(avalon);
+		goto out;
+	}
+
+	avalon_free_work(thr, info->bulk0);
+
+	record_temp_fan(info, &ar, &(avalon->temp));
+	applog(LOG_INFO,
+	       "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t"
+	       "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC",
+	       info->fan0, info->fan1, info->fan2,
+	       info->temp0, info->temp1, info->temp2, info->temp_max);
+	info->temp_history_index++;
+	info->temp_sum += info->temp2;
+	applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d",
+	       info->temp_history_index, info->temp_history_count, info->temp_old);
+	if (info->temp_history_index == info->temp_history_count) {
+		adjust_fan(info);
+		info->temp_history_index = 0;
+		info->temp_sum = 0;
+	}
+
+	/*
+	 * FIXME: Each work split to 10 pieces, each piece send to a
+	 * asic(256MHs). one work can be mulit-nonce back. it is not
+	 * easy calculate correct hash on such situation. so I simplely
+	 * add each nonce to hash_count. base on Utility/m hash_count*2
+	 * give a very good result.
+	 *
+	 * Any patch will be great.
+	 */
+	scanret = hash_count * 2;
+out:
+	avalon_free_work(thr, avalon->works);
+	avalon->queued = 0;
+	return scanret;
+}
+
+static struct api_data *avalon_api_stats(struct cgpu_info *cgpu)
+{
+	struct api_data *root = NULL;
+	struct avalon_info *info = avalon_info[cgpu->device_id];
+
+	root = api_add_int(root, "baud", &(info->baud), false);
+	root = api_add_int(root, "miner_count", &(info->miner_count),false);
+	root = api_add_int(root, "asic_count", &(info->asic_count), false);
+	root = api_add_int(root, "read_count", &(info->read_count), false);
+	root = api_add_int(root, "timeout", &(info->timeout), false);
+	root = api_add_int(root, "frequency", &(info->frequency), false);
+
+	root = api_add_int(root, "fan1", &(info->fan0), false);
+	root = api_add_int(root, "fan2", &(info->fan1), false);
+	root = api_add_int(root, "fan3", &(info->fan2), false);
+
+	root = api_add_int(root, "temp1", &(info->temp0), false);
+	root = api_add_int(root, "temp2", &(info->temp1), false);
+	root = api_add_int(root, "temp3", &(info->temp2), false);
+	root = api_add_int(root, "temp_max", &(info->temp_max), false);
+
+	root = api_add_int(root, "no_matching_work", &(info->no_matching_work), false);
+	root = api_add_int(root, "matching_work_count1", &(info->matching_work[0]), false);
+	root = api_add_int(root, "matching_work_count2", &(info->matching_work[1]), false);
+	root = api_add_int(root, "matching_work_count3", &(info->matching_work[2]), false);
+	root = api_add_int(root, "matching_work_count4", &(info->matching_work[3]), false);
+	root = api_add_int(root, "matching_work_count5", &(info->matching_work[4]), false);
+	root = api_add_int(root, "matching_work_count6", &(info->matching_work[5]), false);
+	root = api_add_int(root, "matching_work_count7", &(info->matching_work[6]), false);
+	root = api_add_int(root, "matching_work_count8", &(info->matching_work[7]), false);
+	root = api_add_int(root, "matching_work_count9", &(info->matching_work[8]), false);
+	root = api_add_int(root, "matching_work_count10", &(info->matching_work[9]), false);
+	root = api_add_int(root, "matching_work_count11", &(info->matching_work[10]), false);
+	root = api_add_int(root, "matching_work_count12", &(info->matching_work[11]), false);
+	root = api_add_int(root, "matching_work_count13", &(info->matching_work[12]), false);
+	root = api_add_int(root, "matching_work_count14", &(info->matching_work[13]), false);
+	root = api_add_int(root, "matching_work_count15", &(info->matching_work[14]), false);
+	root = api_add_int(root, "matching_work_count16", &(info->matching_work[15]), false);
+	root = api_add_int(root, "matching_work_count17", &(info->matching_work[16]), false);
+	root = api_add_int(root, "matching_work_count18", &(info->matching_work[17]), false);
+	root = api_add_int(root, "matching_work_count19", &(info->matching_work[18]), false);
+	root = api_add_int(root, "matching_work_count20", &(info->matching_work[19]), false);
+	root = api_add_int(root, "matching_work_count21", &(info->matching_work[20]), false);
+	root = api_add_int(root, "matching_work_count22", &(info->matching_work[21]), false);
+	root = api_add_int(root, "matching_work_count23", &(info->matching_work[22]), false);
+	root = api_add_int(root, "matching_work_count24", &(info->matching_work[23]), false);
+
+	return root;
+}
+
+static void avalon_shutdown(struct thr_info *thr)
+{
+	do_avalon_close(thr);
+}
+
+struct device_drv avalon_api = {
+	.dname = "avalon",
+	.name = "AVA",
+	.drv_detect = avalon_detect,
+	.thread_prepare = avalon_prepare,
+	.hash_work = hash_queued_work,
+	.queue_full = avalon_fill,
+	.scanwork = avalon_scanhash,
+	.get_api_stats = avalon_api_stats,
+	.reinit_device = avalon_init,
+	.thread_shutdown = avalon_shutdown,
+};

+ 136 - 0
driver-avalon.h

@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013 Avalon project
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#ifndef AVALON_H
+#define AVALON_H
+
+#ifdef USE_AVALON
+
+#define AVALON_TIME_FACTOR 10
+#define AVALON_RESET_FAULT_DECISECONDS 1
+#define AVALON_MINER_THREADS 1
+
+#define AVALON_IO_SPEED		115200
+#define AVALON_HASH_TIME_FACTOR	((float)1.67/0x32)
+#define AVALON_RESET_PITCH	(300*1000*1000)
+
+#define AVALON_FAN_FACTOR 120
+#define AVALON_DEFAULT_FAN_MAX_PWM 0xA0 /* 100% */
+#define AVALON_DEFAULT_FAN_MIN_PWM 0x20 /*  20% */
+
+#define AVALON_DEFAULT_TIMEOUT 0x32
+#define AVALON_DEFAULT_FREQUENCY 256
+#define AVALON_DEFAULT_MINER_NUM 0x20
+#define AVALON_DEFAULT_ASIC_NUM 0xA
+
+struct avalon_task {
+	uint8_t reset		:1;
+	uint8_t flush_fifo	:1;
+	uint8_t fan_eft		:1;
+	uint8_t timer_eft	:1;
+	uint8_t asic_num	:4;
+	uint8_t fan_pwm_data;
+	uint8_t timeout_data;
+	uint8_t miner_num;
+
+	uint8_t nonce_elf		:1;
+	uint8_t gate_miner_elf		:1;
+	uint8_t asic_pll		:1;
+	uint8_t gate_miner		:1;
+	uint8_t _pad0			:4;
+	uint8_t _pad1[3];
+	uint32_t _pad2;
+
+	uint8_t midstate[32];
+	uint8_t data[12];
+} __attribute__((packed, aligned(4)));
+
+struct avalon_result {
+	uint32_t nonce;
+	uint8_t data[12];
+	uint8_t midstate[32];
+
+	uint8_t fan0;
+	uint8_t fan1;
+	uint8_t fan2;
+	uint8_t temp0;
+	uint8_t temp1;
+	uint8_t temp2;
+	uint8_t _pad0[2];
+
+	uint16_t fifo_wp;
+	uint16_t fifo_rp;
+	uint8_t chip_num;
+	uint8_t pwm_data;
+	uint8_t timeout;
+	uint8_t miner_num;
+} __attribute__((packed, aligned(4)));
+
+struct avalon_info {
+	int read_count;
+
+	int baud;
+	int miner_count;
+	int asic_count;
+	int timeout;
+
+	int fan0;
+	int fan1;
+	int fan2;
+
+	int temp0;
+	int temp1;
+	int temp2;
+	int temp_max;
+	int temp_history_count;
+	int temp_history_index;
+	int temp_sum;
+	int temp_old;
+	int fan_pwm;
+
+	int no_matching_work;
+	int matching_work[AVALON_DEFAULT_MINER_NUM];
+	struct work *bulk0[AVALON_DEFAULT_MINER_NUM];
+	struct work *bulk1[AVALON_DEFAULT_MINER_NUM];
+	struct work *bulk2[AVALON_DEFAULT_MINER_NUM];
+	struct work *bulk3[AVALON_DEFAULT_MINER_NUM];
+
+	int frequency;
+};
+
+#define AVALON_WRITE_SIZE (sizeof(struct avalon_task))
+#define AVALON_READ_SIZE (sizeof(struct avalon_result))
+
+#define AVA_GETS_ERROR -1
+#define AVA_GETS_OK 0
+#define AVA_GETS_RESTART 1
+#define AVA_GETS_TIMEOUT 2
+
+#define AVA_SEND_ERROR -1
+#define AVA_SEND_OK 0
+#define AVA_SEND_BUFFER_EMPTY 1
+#define AVA_SEND_BUFFER_FULL 2
+
+#define AVA_BUFFER_FULL 0
+#define AVA_BUFFER_EMPTY 1
+
+#define avalon_open2(devpath, baud, purge)  serial_open(devpath, baud, AVALON_RESET_FAULT_DECISECONDS, purge)
+#define avalon_open(devpath, baud)  avalon_open2(devpath, baud, true)
+#define avalon_close(fd) close(fd)
+
+#define avalon_buffer_full(fd)	get_serial_cts(fd)
+
+#define AVALON_READ_TIME(baud) ((double)AVALON_READ_SIZE * (double)8.0 / (double)(baud))
+#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1]
+ASSERT1(sizeof(uint32_t) == 4);
+
+extern struct avalon_info **avalon_info;
+
+#endif /* USE_AVALON */
+#endif	/* AVALON_H */

+ 22 - 0
fpgautils.c

@@ -1,4 +1,5 @@
 /*
+ * Copyright 2013 Con Kolivas <kernel@kolivas.org>
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Andrew Smith
  *
@@ -19,6 +20,7 @@
 #ifndef WIN32
 #include <errno.h>
 #include <termios.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -32,10 +34,12 @@
 
 #ifdef HAVE_LIBUDEV
 #include <libudev.h>
+#include <sys/ioctl.h>
 #endif
 
 #include "elist.h"
 #include "logging.h"
+#include "miner.h"
 #include "fpgautils.h"
 
 #ifdef HAVE_LIBUDEV
@@ -382,6 +386,14 @@ int serial_open(const char *devpath, unsigned long baud, signed short timeout, b
 	switch (baud) {
 	case 0:
 		break;
+	case 19200:
+		cfsetispeed(&my_termios, B19200);
+		cfsetospeed(&my_termios, B19200);
+		break;
+	case 38400:
+		cfsetispeed(&my_termios, B38400);
+		cfsetospeed(&my_termios, B38400);
+		break;
 	case 57600:
 		cfsetispeed(&my_termios, B57600);
 		cfsetospeed(&my_termios, B57600);
@@ -570,4 +582,14 @@ size_t _select_write(int fd, char *buf, size_t siz, struct timeval *timeout)
 	return wrote;
 }
 
+int get_serial_cts(int fd)
+{
+	int flags;
+
+	if (!fd)
+		return -1;
+
+	ioctl(fd, TIOCMGET, &flags);
+	return (flags & TIOCM_CTS) ? 1 : 0;
+}
 #endif // ! WIN32

+ 2 - 0
fpgautils.h

@@ -36,6 +36,8 @@ extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
 
 extern FILE *open_bitstream(const char *dname, const char *filename);
 
+extern int get_serial_cts(int fd);
+
 #ifndef WIN32
 extern const struct timeval tv_timeout_default;
 extern const struct timeval tv_inter_char_default;

+ 77 - 0
hexdump.c

@@ -0,0 +1,77 @@
+/*
+ * hexdump implementation without depenecies to *printf()
+ * output is equal to 'hexdump -C'
+ * should be compatible to 64bit architectures
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define hex_print(p) applog(LOG_DEBUG, "%s", p)
+
+static char nibble[] = {
+	'0', '1', '2', '3', '4', '5', '6', '7',
+	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+#define BYTES_PER_LINE 0x10
+
+void hexdump(const uint8_t *p, unsigned int len)
+{
+	unsigned int i, addr;
+	unsigned int wordlen = sizeof(void*);
+	unsigned char v, line[BYTES_PER_LINE * 5];
+
+	for (addr = 0; addr < len; addr += BYTES_PER_LINE) {
+		/* clear line */
+		for (i = 0; i < sizeof(line); i++) {
+			if (i == wordlen * 2 + 52 ||
+			    i == wordlen * 2 + 69) {
+			    	line[i] = '|';
+				continue;
+			}
+
+			if (i == wordlen * 2 + 70) {
+				line[i] = '\0';
+				continue;
+			}
+
+			line[i] = ' ';
+		}
+
+		/* print address */
+		for (i = 0; i < wordlen * 2; i++) {
+			v = addr >> ((wordlen * 2 - i - 1) * 4);
+			line[i] = nibble[v & 0xf];
+		}
+
+		/* dump content */
+		for (i = 0; i < BYTES_PER_LINE; i++) {
+			int pos = (wordlen * 2) + 3 + (i / 8);
+
+			if (addr + i >= len)
+				break;
+
+			v = p[addr + i];
+			line[pos + (i * 3) + 0] = nibble[v >> 4];
+			line[pos + (i * 3) + 1] = nibble[v & 0xf];
+
+			/* character printable? */
+			line[(wordlen * 2) + 53 + i] =
+				(v >= ' ' && v <= '~') ? v : '.';
+		}
+
+		hex_print(line);
+	}
+}

+ 13 - 4
miner.h

@@ -120,9 +120,11 @@ static inline int fsync (int fd)
 
 #if (!defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) \
     || (defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
-#define bswap_16 __builtin_bswap16
-#define bswap_32 __builtin_bswap32
-#define bswap_64 __builtin_bswap64
+#ifndef bswap_16
+ #define bswap_16 __builtin_bswap16
+ #define bswap_32 __builtin_bswap32
+ #define bswap_64 __builtin_bswap64
+#endif
 #else
 #if HAVE_BYTESWAP_H
 #include <byteswap.h>
@@ -421,8 +423,12 @@ struct cgpu_info {
 #ifdef USE_USBUTILS
 		struct cg_usb_device *usbdev;
 #endif
-#ifdef USE_ICARUS
+#if defined(USE_ICARUS) || defined(USE_AVALON)
 		int device_fd;
+#endif
+#ifdef USE_AVALON
+	struct work **works;
+	int queued;
 #endif
 	};
 #ifdef USE_USBUTILS
@@ -786,6 +792,9 @@ extern bool opt_restart;
 extern char *opt_icarus_options;
 extern char *opt_icarus_timing;
 extern bool opt_worktime;
+#ifdef USE_AVALON
+extern char *opt_avalon_options;
+#endif
 #ifdef USE_USBUTILS
 extern char *opt_usb_select;
 extern int opt_usbdump;