|
|
@@ -173,6 +173,11 @@ static void rev(unsigned char *s, size_t l)
|
|
|
#define icarus_open2(devpath, baud, purge) serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge)
|
|
|
#define icarus_open(devpath, baud) icarus_open2(devpath, baud, false)
|
|
|
|
|
|
+#define ICA_GETS_ERROR -1
|
|
|
+#define ICA_GETS_OK 0
|
|
|
+#define ICA_GETS_RESTART 1
|
|
|
+#define ICA_GETS_TIMEOUT 2
|
|
|
+
|
|
|
int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count)
|
|
|
{
|
|
|
ssize_t ret = 0;
|
|
|
@@ -188,13 +193,13 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
|
|
|
};
|
|
|
struct epoll_event evr[2];
|
|
|
int epoll_timeout = ICARUS_READ_FAULT_DECISECONDS * 100;
|
|
|
+ if (thr && thr->work_restart_fd != -1) {
|
|
|
epollfd = epoll_create(2);
|
|
|
if (epollfd != -1) {
|
|
|
if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
|
|
|
close(epollfd);
|
|
|
epollfd = -1;
|
|
|
}
|
|
|
- if (thr->work_restart_fd != -1)
|
|
|
{
|
|
|
ev.data.fd = thr->work_restart_fd;
|
|
|
if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, thr->work_restart_fd, &ev))
|
|
|
@@ -208,6 +213,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
|
|
|
}
|
|
|
else
|
|
|
applog(LOG_ERR, "Icarus: Error creating epoll");
|
|
|
+ }
|
|
|
#endif
|
|
|
|
|
|
// Read reply 1 byte at a time to get earliest tv_finish
|
|
|
@@ -228,6 +234,8 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
|
|
|
else
|
|
|
#endif
|
|
|
ret = read(fd, buf, 1);
|
|
|
+ if (ret < 0)
|
|
|
+ return ICA_GETS_ERROR;
|
|
|
|
|
|
if (first)
|
|
|
gettimeofday(tv_finish, NULL);
|
|
|
@@ -236,7 +244,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
|
|
|
{
|
|
|
if (epollfd != -1)
|
|
|
close(epollfd);
|
|
|
- return 0;
|
|
|
+ return ICA_GETS_OK;
|
|
|
}
|
|
|
|
|
|
if (ret > 0) {
|
|
|
@@ -247,17 +255,26 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
|
|
|
}
|
|
|
|
|
|
rc++;
|
|
|
- if (rc >= read_count || thr->work_restart) {
|
|
|
+ if (rc >= read_count) {
|
|
|
+ if (epollfd != -1)
|
|
|
+ close(epollfd);
|
|
|
+ if (opt_debug) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "Icarus Read: No data in %.2f seconds",
|
|
|
+ (float)rc/(float)TIME_FACTOR);
|
|
|
+ }
|
|
|
+ return ICA_GETS_TIMEOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (thr && thr->work_restart) {
|
|
|
if (epollfd != -1)
|
|
|
close(epollfd);
|
|
|
if (opt_debug) {
|
|
|
- rc *= ICARUS_READ_FAULT_DECISECONDS;
|
|
|
applog(LOG_DEBUG,
|
|
|
- "Icarus Read: %s %d.%d seconds",
|
|
|
- thr->work_restart ? "Work restart at" : "No data in",
|
|
|
- rc / 10, rc % 10);
|
|
|
+ "Icarus Read: Work restart at %.2f seconds",
|
|
|
+ (float)(rc)/(float)TIME_FACTOR);
|
|
|
}
|
|
|
- return 1;
|
|
|
+ return ICA_GETS_RESTART;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -275,6 +292,13 @@ static int icarus_write(int fd, const void *buf, size_t bufLen)
|
|
|
|
|
|
#define icarus_close(fd) close(fd)
|
|
|
|
|
|
+static void do_icarus_close(struct thr_info *thr)
|
|
|
+{
|
|
|
+ struct cgpu_info *icarus = thr->cgpu;
|
|
|
+ icarus_close(icarus->device_fd);
|
|
|
+ icarus->device_fd = -1;
|
|
|
+}
|
|
|
+
|
|
|
static const char *timing_mode_str(enum timing_mode timing_mode)
|
|
|
{
|
|
|
switch(timing_mode) {
|
|
|
@@ -468,6 +492,7 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info)
|
|
|
*(colon2++) = '\0';
|
|
|
|
|
|
if (*colon) {
|
|
|
+ info->user_set |= 1;
|
|
|
tmp = atoi(colon);
|
|
|
if (tmp == 1 || tmp == 2 || tmp == 4 || tmp == 8) {
|
|
|
*work_division = tmp;
|
|
|
@@ -484,6 +509,7 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info)
|
|
|
*(colon++) = '\0';
|
|
|
|
|
|
if (*colon2) {
|
|
|
+ info->user_set |= 2;
|
|
|
tmp = atoi(colon2);
|
|
|
if (tmp > 0 && tmp <= *work_division)
|
|
|
*fpga_count = tmp;
|
|
|
@@ -555,11 +581,7 @@ bool icarus_detect_custom(const char *devpath, struct device_api *api, struct IC
|
|
|
gettimeofday(&tv_start, NULL);
|
|
|
|
|
|
memset(nonce_bin, 0, sizeof(nonce_bin));
|
|
|
- struct thr_info dummy = {
|
|
|
- .work_restart = false,
|
|
|
- .work_restart_fd = -1,
|
|
|
- };
|
|
|
- icarus_gets(nonce_bin, fd, &tv_finish, &dummy, 1);
|
|
|
+ icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1);
|
|
|
|
|
|
icarus_close(fd);
|
|
|
|
|
|
@@ -592,6 +614,7 @@ bool icarus_detect_custom(const char *devpath, struct device_api *api, struct IC
|
|
|
icarus = calloc(1, sizeof(struct cgpu_info));
|
|
|
icarus->api = api;
|
|
|
icarus->device_path = strdup(devpath);
|
|
|
+ icarus->device_fd = -1;
|
|
|
icarus->threads = 1;
|
|
|
add_cgpu(icarus);
|
|
|
|
|
|
@@ -647,6 +670,8 @@ static bool icarus_prepare(struct thr_info *thr)
|
|
|
|
|
|
struct timeval now;
|
|
|
|
|
|
+ icarus->device_fd = -1;
|
|
|
+
|
|
|
int fd = icarus_open2(icarus->device_path, info->baud, true);
|
|
|
if (unlikely(-1 == fd)) {
|
|
|
applog(LOG_ERR, "Failed to open Icarus on %s",
|
|
|
@@ -685,23 +710,58 @@ static bool icarus_reopen(struct cgpu_info *icarus, struct icarus_state *state,
|
|
|
*fdp = icarus->device_fd = icarus_open(icarus->device_path, info->baud);
|
|
|
if (unlikely(-1 == *fdp)) {
|
|
|
applog(LOG_ERR, "%s %u: Failed to reopen on %s", icarus->api->name, icarus->device_id, icarus->device_path);
|
|
|
+ icarus->device_last_not_well = time(NULL);
|
|
|
+ icarus->device_not_well_reason = REASON_DEV_COMMS_ERROR;
|
|
|
+ icarus->dev_comms_error_count++;
|
|
|
state->firstrun = true;
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
|
|
|
+{
|
|
|
+ struct cgpu_info *icarus = thr->cgpu;
|
|
|
+ struct icarus_state *state = thr->cgpu_data;
|
|
|
+ int fd = icarus->device_fd;
|
|
|
+ int ret;
|
|
|
+ char *ob_hex;
|
|
|
+
|
|
|
+ gettimeofday(&state->tv_workstart, NULL);
|
|
|
+
|
|
|
+ ret = icarus_write(fd, ob_bin, 64);
|
|
|
+ if (ret) {
|
|
|
+ do_icarus_close(thr);
|
|
|
+ applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
|
|
|
+ icarus->device_last_not_well = time(NULL);
|
|
|
+ icarus->device_not_well_reason = REASON_DEV_COMMS_ERROR;
|
|
|
+ icarus->dev_comms_error_count++;
|
|
|
+ return false; /* This should never happen */
|
|
|
+ }
|
|
|
+
|
|
|
+ if (opt_debug) {
|
|
|
+ ob_hex = bin2hex(ob_bin, 64);
|
|
|
+ if (ob_hex) {
|
|
|
+ applog(LOG_DEBUG, "%s %u sent: %s",
|
|
|
+ icarus->api->name,
|
|
|
+ icarus->device_id, ob_hex);
|
|
|
+ free(ob_hex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
__maybe_unused int64_t max_nonce)
|
|
|
{
|
|
|
struct cgpu_info *icarus;
|
|
|
int fd;
|
|
|
- int ret, lret;
|
|
|
+ int ret;
|
|
|
|
|
|
struct ICARUS_INFO *info;
|
|
|
|
|
|
unsigned char ob_bin[64] = {0}, nonce_bin[ICARUS_READ_SIZE] = {0};
|
|
|
- char *ob_hex;
|
|
|
uint32_t nonce;
|
|
|
int64_t hash_count;
|
|
|
struct timeval tv_start, elapsed;
|
|
|
@@ -726,6 +786,13 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
// Prepare the next work immediately
|
|
|
memcpy(ob_bin, work->midstate, 32);
|
|
|
memcpy(ob_bin + 52, work->data + 64, 12);
|
|
|
+ if (!(memcmp(&ob_bin[56], "\xff\xff\xff\xff", 4)
|
|
|
+ || memcmp(&ob_bin, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32))) {
|
|
|
+ // This sequence is used on cairnsmore bitstreams for commands, NEVER send it otherwise
|
|
|
+ applog(LOG_WARNING, "%s %u: Received job attempting to send a command, corrupting it!",
|
|
|
+ icarus->api->name, icarus->device_id);
|
|
|
+ ob_bin[56] = 0;
|
|
|
+ }
|
|
|
rev(ob_bin, 32);
|
|
|
rev(ob_bin + 52, 12);
|
|
|
|
|
|
@@ -737,26 +804,34 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
if (state->changework)
|
|
|
{
|
|
|
state->changework = false;
|
|
|
- lret = 1;
|
|
|
+ ret = ICA_GETS_RESTART;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */
|
|
|
- lret = icarus_gets(nonce_bin, fd, &state->tv_workfinish, thr, info->read_count);
|
|
|
- if (lret) {
|
|
|
- if (thr->work_restart) {
|
|
|
-
|
|
|
- // The prepared work is invalid, and the current work is abandoned
|
|
|
- // Go back to the main loop to get the next work, and stuff
|
|
|
- // Returning to the main loop will clear work_restart, so use a flag...
|
|
|
- state->changework = true;
|
|
|
- return 0;
|
|
|
-
|
|
|
- }
|
|
|
- if (info->quirk_reopen == 1 && !icarus_reopen(icarus, state, &fd))
|
|
|
+ ret = icarus_gets(nonce_bin, fd, &state->tv_workfinish, thr, info->read_count);
|
|
|
+ switch (ret) {
|
|
|
+ case ICA_GETS_RESTART:
|
|
|
+ // The prepared work is invalid, and the current work is abandoned
|
|
|
+ // Go back to the main loop to get the next work, and stuff
|
|
|
+ // Returning to the main loop will clear work_restart, so use a flag...
|
|
|
+ state->changework = true;
|
|
|
return 0;
|
|
|
+ case ICA_GETS_ERROR:
|
|
|
+ do_icarus_close(thr);
|
|
|
+ applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
|
|
|
+ icarus->device_last_not_well = time(NULL);
|
|
|
+ icarus->device_not_well_reason = REASON_DEV_COMMS_ERROR;
|
|
|
+ icarus->dev_comms_error_count++;
|
|
|
+ if (!icarus_reopen(icarus, state, &fd))
|
|
|
+ return -1;
|
|
|
+ break;
|
|
|
+ case ICA_GETS_TIMEOUT:
|
|
|
+ if (info->quirk_reopen == 1 && !icarus_reopen(icarus, state, &fd))
|
|
|
+ return -1;
|
|
|
+ case ICA_GETS_OK:
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
tv_start = state->tv_workstart;
|
|
|
@@ -764,7 +839,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
}
|
|
|
else
|
|
|
if (fd == -1 && !icarus_reopen(icarus, state, &fd))
|
|
|
- return 0;
|
|
|
+ return -1;
|
|
|
|
|
|
#ifndef WIN32
|
|
|
tcflush(fd, TCOFLUSH);
|
|
|
@@ -774,35 +849,23 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
nonce = be32toh(nonce);
|
|
|
|
|
|
// Handle dynamic clocking for "subclass" devices
|
|
|
- // This runs before sending next job, in case it isn't supported
|
|
|
+ // This needs to run before sending next job, since it hashes the command too
|
|
|
if (info->dclk.freqM && likely(!state->firstrun)) {
|
|
|
- dclk_gotNonces(&info->dclk);
|
|
|
+ int qsec = ((4 * elapsed.tv_sec) + (elapsed.tv_usec / 250000)) ?: 1;
|
|
|
+ for (int n = qsec; n; --n)
|
|
|
+ dclk_gotNonces(&info->dclk);
|
|
|
if (nonce && !test_nonce(&state->last_work, nonce, false))
|
|
|
- dclk_errorCount(&info->dclk, 1.0);
|
|
|
+ dclk_errorCount(&info->dclk, qsec);
|
|
|
dclk_preUpdate(&info->dclk);
|
|
|
dclk_updateFreq(&info->dclk, info->dclk_change_clock_func, thr);
|
|
|
}
|
|
|
|
|
|
- gettimeofday(&state->tv_workstart, NULL);
|
|
|
-
|
|
|
- ret = icarus_write(fd, ob_bin, sizeof(ob_bin));
|
|
|
- if (ret) {
|
|
|
- icarus_close(fd);
|
|
|
- return -1; /* This should never happen */
|
|
|
- }
|
|
|
-
|
|
|
- if (opt_debug) {
|
|
|
- ob_hex = bin2hex(ob_bin, sizeof(ob_bin));
|
|
|
- if (ob_hex) {
|
|
|
- applog(LOG_DEBUG, "%s %u sent: %s",
|
|
|
- icarus->api->name,
|
|
|
- icarus->device_id, ob_hex);
|
|
|
- free(ob_hex);
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!icarus_start_work(thr, ob_bin))
|
|
|
+ /* This should never happen */
|
|
|
+ state->firstrun = true;
|
|
|
|
|
|
if (info->quirk_reopen == 2 && !icarus_reopen(icarus, state, &fd))
|
|
|
- return 0;
|
|
|
+ state->firstrun = true;
|
|
|
|
|
|
work->blk.nonce = 0xffffffff;
|
|
|
|
|
|
@@ -815,7 +878,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
// OK, done starting Icarus's next job... now process the last run's result!
|
|
|
|
|
|
// aborted before becoming idle, get new work
|
|
|
- if (nonce == 0 && lret) {
|
|
|
+ if (ret == ICA_GETS_TIMEOUT || ret == ICA_GETS_RESTART) {
|
|
|
memcpy(&state->last_work, work, sizeof(state->last_work));
|
|
|
// ONLY up to just when it aborted
|
|
|
// We didn't read a reply so we don't subtract ICARUS_READ_TIME
|
|
|
@@ -842,6 +905,16 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
was_hw_error = (curr_hw_errors > icarus->hw_errors);
|
|
|
memcpy(&state->last_work, work, sizeof(state->last_work));
|
|
|
|
|
|
+ // Force a USB close/reopen on any hw error
|
|
|
+ if (was_hw_error)
|
|
|
+ if (info->quirk_reopen != 2) {
|
|
|
+ if (!icarus_reopen(icarus, state, &fd))
|
|
|
+ state->firstrun = true;
|
|
|
+ // Some devices (Cairnsmore1, for example) abort hashing when reopened, so send the job again
|
|
|
+ if (!icarus_start_work(thr, ob_bin))
|
|
|
+ state->firstrun = true;
|
|
|
+ }
|
|
|
+
|
|
|
hash_count = (nonce & info->nonce_mask);
|
|
|
hash_count++;
|
|
|
hash_count *= info->fpga_count;
|
|
|
@@ -970,8 +1043,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
|
|
|
else if (info->timing_mode == MODE_SHORT)
|
|
|
info->do_icarus_timing = false;
|
|
|
|
|
|
-// applog(LOG_WARNING, "%s %u Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->api->name, icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
|
|
|
- applog(LOG_WARNING, "%s %u Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
|
|
|
+// applog(LOG_DEBUG, "%s %u Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->api->name, icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
|
|
|
+ applog(LOG_DEBUG, "%s %u Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
|
|
|
icarus->api->name,
|
|
|
icarus->device_id, Hs, W, read_count, fullnonce);
|
|
|
}
|
|
|
@@ -1016,8 +1089,7 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu)
|
|
|
|
|
|
static void icarus_shutdown(struct thr_info *thr)
|
|
|
{
|
|
|
- struct cgpu_info *icarus = thr->cgpu;
|
|
|
- icarus_close(icarus->device_fd);
|
|
|
+ do_icarus_close(thr);
|
|
|
free(thr->cgpu_data);
|
|
|
}
|
|
|
|