|
|
@@ -23,9 +23,22 @@
|
|
|
#include <sys/un.h>
|
|
|
|
|
|
#include "deviceapi.h"
|
|
|
+#include "logging.h"
|
|
|
#include "miner.h"
|
|
|
|
|
|
+#define MINERGATE_PROTOCOL_VER 6
|
|
|
+#define MINERGATE_MAGIC 0xcaf4
|
|
|
static const int minergate_max_responses = 300;
|
|
|
+#define MINERGATE_PKT_HEADER_SZ 8
|
|
|
+#define MINERGATE_PKT_REQ_ITEM_SZ 0x34
|
|
|
+#define MINERGATE_PKT_REQ_MAX 100
|
|
|
+#define MINERGATE_PKT_RSP_ITEM_SZ 0x14
|
|
|
+#define MINERGATE_PKT_RSP_MAX 300
|
|
|
+#define MINERGATE_POLL_US 100000
|
|
|
+#define MINERGATE_RETRY_US 5000000
|
|
|
+
|
|
|
+#define MINERGATE_PKT_REQ_SZ (MINERGATE_PKT_HEADER_SZ + (MINERGATE_PKT_REQ_ITEM_SZ * MINERGATE_PKT_REQ_MAX))
|
|
|
+#define MINERGATE_PKT_RSP_SZ (MINERGATE_PKT_HEADER_SZ + (MINERGATE_PKT_RSP_ITEM_SZ * MINERGATE_PKT_RSP_MAX))
|
|
|
|
|
|
BFG_REGISTER_DRIVER(minergate_drv)
|
|
|
|
|
|
@@ -34,6 +47,12 @@ enum minergate_reqpkt_flags {
|
|
|
MRPF_FLUSH = 2,
|
|
|
};
|
|
|
|
|
|
+struct minergate_state {
|
|
|
+ work_device_id_t next_jobid;
|
|
|
+ unsigned ready_to_queue;
|
|
|
+ uint8_t *req_buffer;
|
|
|
+};
|
|
|
+
|
|
|
static
|
|
|
int minergate_open(const char * const devpath)
|
|
|
{
|
|
|
@@ -57,6 +76,27 @@ int minergate_open(const char * const devpath)
|
|
|
return fd;
|
|
|
}
|
|
|
|
|
|
+static
|
|
|
+ssize_t minergate_read(const int fd, void * const buf_p, size_t bufLen)
|
|
|
+{
|
|
|
+ uint8_t *buf = buf_p;
|
|
|
+ ssize_t rv, ret = 0;
|
|
|
+ while (bufLen > 0)
|
|
|
+ {
|
|
|
+ rv = read(fd, buf, bufLen);
|
|
|
+ if (rv <= 0)
|
|
|
+ {
|
|
|
+ if (ret > 0)
|
|
|
+ return ret;
|
|
|
+ return rv;
|
|
|
+ }
|
|
|
+ buf += rv;
|
|
|
+ bufLen -= rv;
|
|
|
+ ret += rv;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static
|
|
|
bool minergate_detect_one(const char * const devpath)
|
|
|
{
|
|
|
@@ -66,11 +106,9 @@ bool minergate_detect_one(const char * const devpath)
|
|
|
applogr(false, LOG_DEBUG, "%s: %s: Cannot connect", minergate_drv.dname, devpath);
|
|
|
|
|
|
int epfd = -1;
|
|
|
- static const int minergate_version = 6;
|
|
|
-#define req_sz (8 + (24 * 100))
|
|
|
- uint8_t buf[req_sz] = {0xbf, 0x90, minergate_version, MRPF_FIRST, 0,0, 0 /* req count */,};
|
|
|
- pk_u16le(buf, 4, 0xcaf4);
|
|
|
- if (req_sz != write(fd, buf, req_sz))
|
|
|
+ uint8_t buf[MINERGATE_PKT_REQ_SZ] = {0xbf, 0x90, MINERGATE_PROTOCOL_VER, MRPF_FIRST, 0,0, 0 /* req count */,};
|
|
|
+ pk_u16le(buf, 4, MINERGATE_MAGIC);
|
|
|
+ if (MINERGATE_PKT_REQ_SZ != write(fd, buf, MINERGATE_PKT_REQ_SZ))
|
|
|
return_via_applog(out, , LOG_DEBUG, "%s: %s: write incomplete or failed", minergate_drv.dname, devpath);
|
|
|
|
|
|
epfd = epoll_create(1);
|
|
|
@@ -83,7 +121,7 @@ bool minergate_detect_one(const char * const devpath)
|
|
|
return_via_applog(out, , LOG_DEBUG, "%s: %s: %s failed", minergate_drv.dname, devpath, "epoll_ctl");
|
|
|
|
|
|
size_t read_bytes = 0;
|
|
|
- static const size_t read_expect = 8;
|
|
|
+ static const size_t read_expect = MINERGATE_PKT_HEADER_SZ;
|
|
|
ssize_t r;
|
|
|
while (read_bytes < read_expect)
|
|
|
{
|
|
|
@@ -97,9 +135,9 @@ bool minergate_detect_one(const char * const devpath)
|
|
|
|
|
|
if (buf[1] != 0x90)
|
|
|
return_via_applog(out, , LOG_DEBUG, "%s: %s: %s mismatch", minergate_drv.dname, devpath, "request_id");
|
|
|
- if (buf[2] != minergate_version)
|
|
|
- return_via_applog(out, , LOG_DEBUG, "%s: %s: %s mismatch", minergate_drv.dname, devpath, "minergate_version");
|
|
|
- if (upk_u16le(buf, 4) != 0xcaf4)
|
|
|
+ if (buf[2] != MINERGATE_PROTOCOL_VER)
|
|
|
+ return_via_applog(out, , LOG_DEBUG, "%s: %s: %s mismatch", minergate_drv.dname, devpath, "Protocol version");
|
|
|
+ if (upk_u16le(buf, 4) != MINERGATE_MAGIC)
|
|
|
return_via_applog(out, , LOG_DEBUG, "%s: %s: %s mismatch", minergate_drv.dname, devpath, "magic");
|
|
|
|
|
|
uint16_t responses = upk_u16le(buf, 6);
|
|
|
@@ -111,6 +149,7 @@ bool minergate_detect_one(const char * const devpath)
|
|
|
.drv = &minergate_drv,
|
|
|
.device_path = strdup(devpath),
|
|
|
.deven = DEV_ENABLED,
|
|
|
+ .threads = 1,
|
|
|
};
|
|
|
rv = add_cgpu(cgpu);
|
|
|
|
|
|
@@ -133,8 +172,171 @@ void minergate_detect(void)
|
|
|
generic_detect(&minergate_drv, minergate_detect_one, minergate_detect_auto, 0);
|
|
|
}
|
|
|
|
|
|
+static
|
|
|
+bool minergate_init(struct thr_info * const thr)
|
|
|
+{
|
|
|
+ struct cgpu_info * const dev = thr->cgpu;
|
|
|
+
|
|
|
+ const int fd = minergate_open(dev->device_path);
|
|
|
+ dev->device_fd = fd;
|
|
|
+ if (fd < 0)
|
|
|
+ applogr(false, LOG_ERR, "%s: Cannot connect", dev->dev_repr);
|
|
|
+
|
|
|
+ struct minergate_state * const state = malloc(sizeof(*state) + MINERGATE_PKT_REQ_SZ);
|
|
|
+ if (!state)
|
|
|
+ applogr(false, LOG_ERR, "%s: %s failed", dev->dev_repr, "malloc");
|
|
|
+ *state = (struct minergate_state){
|
|
|
+ .req_buffer = (void*)&state[1]
|
|
|
+ };
|
|
|
+ thr->cgpu_data = state;
|
|
|
+ thr->work = thr->work_list = NULL;
|
|
|
+
|
|
|
+ memset(state->req_buffer, 0, MINERGATE_PKT_REQ_SZ);
|
|
|
+ pk_u8(state->req_buffer, 2, MINERGATE_PROTOCOL_VER);
|
|
|
+ state->req_buffer[3] = MRPF_FIRST;
|
|
|
+ pk_u16le(state->req_buffer, 4, MINERGATE_MAGIC);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+bool minergate_queue_full(struct thr_info * const thr)
|
|
|
+{
|
|
|
+ static const unsigned max_minergate_jobs = 300, max_requests = 100;
|
|
|
+ struct minergate_state * const state = thr->cgpu_data;
|
|
|
+ bool qf;
|
|
|
+
|
|
|
+ if (HASH_COUNT(thr->work) + state->ready_to_queue >= max_minergate_jobs)
|
|
|
+ qf = true;
|
|
|
+ else
|
|
|
+ if (state->ready_to_queue >= max_requests)
|
|
|
+ qf = true;
|
|
|
+ else
|
|
|
+ qf = false;
|
|
|
+
|
|
|
+ thr->queue_full = qf;
|
|
|
+ return qf;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+bool minergate_queue_append(struct thr_info * const thr, struct work * const work)
|
|
|
+{
|
|
|
+ struct minergate_state * const state = thr->cgpu_data;
|
|
|
+
|
|
|
+ if (minergate_queue_full(thr))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ work->device_id = (uint32_t)(state->next_jobid++);
|
|
|
+
|
|
|
+ uint8_t * const my_buf = &state->req_buffer[MINERGATE_PKT_HEADER_SZ + (MINERGATE_PKT_REQ_ITEM_SZ * state->ready_to_queue++)];
|
|
|
+ pk_u32be(my_buf, 0, work->device_id);
|
|
|
+ memcpy(&my_buf[ 4], &work->data[0x48], 4); // nbits
|
|
|
+ memcpy(&my_buf[ 8], &work->data[0x44], 4); // ntime
|
|
|
+ memcpy(&my_buf[0x0c], &work->data[0x40], 4); // merkle-tail
|
|
|
+ memcpy(&my_buf[0x10], work->midstate, 0x20);
|
|
|
+ pk_u8(my_buf, 0x30, 0x20); // search leading zeros
|
|
|
+ pk_u8(my_buf, 0x31, 0); // ntime limit
|
|
|
+ pk_u8(my_buf, 0x32, 0); // ntime offset
|
|
|
+ pk_u8(my_buf, 0x33, 0); // reserved
|
|
|
+
|
|
|
+ HASH_ADD(hh, thr->work, device_id, sizeof(work->device_id), work);
|
|
|
+ LL_PREPEND(thr->work_list, work);
|
|
|
+ timer_set_delay_from_now(&thr->tv_poll, 0);
|
|
|
+ minergate_queue_full(thr);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+void minergate_queue_flush(struct thr_info * const thr)
|
|
|
+{
|
|
|
+ struct minergate_state * const state = thr->cgpu_data;
|
|
|
+
|
|
|
+ // TODO: prune state->ready_to_queue
|
|
|
+ state->req_buffer[3] |= MRPF_FLUSH;
|
|
|
+ timer_set_delay_from_now(&thr->tv_poll, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+void minergate_poll(struct thr_info * const thr)
|
|
|
+{
|
|
|
+ struct cgpu_info * const dev = thr->cgpu;
|
|
|
+ struct minergate_state * const state = thr->cgpu_data;
|
|
|
+ const int fd = dev->device_fd;
|
|
|
+
|
|
|
+ if (opt_dev_protocol || state->ready_to_queue)
|
|
|
+ applog(LOG_DEBUG, "%s: Polling with %u new jobs", dev->dev_repr, state->ready_to_queue);
|
|
|
+ pk_u16le(state->req_buffer, 6, state->ready_to_queue);
|
|
|
+ if (MINERGATE_PKT_REQ_SZ != write(fd, state->req_buffer, MINERGATE_PKT_REQ_SZ))
|
|
|
+ return_via_applog(err, , LOG_ERR, "%s: write incomplete or failed", dev->dev_repr);
|
|
|
+
|
|
|
+ state->req_buffer[3] = 0;
|
|
|
+ state->ready_to_queue = 0;
|
|
|
+ thr->work_list = NULL;
|
|
|
+
|
|
|
+ uint8_t buf[MINERGATE_PKT_RSP_SZ];
|
|
|
+ if (minergate_read(fd, buf, MINERGATE_PKT_RSP_SZ) != MINERGATE_PKT_RSP_SZ)
|
|
|
+ return_via_applog(err, , LOG_ERR, "%s: %s failed", dev->dev_repr, "read");
|
|
|
+
|
|
|
+ if (upk_u8(buf, 2) != MINERGATE_PROTOCOL_VER || upk_u16le(buf, 4) != MINERGATE_MAGIC)
|
|
|
+ return_via_applog(err, , LOG_ERR, "%s: Protocol mismatch", dev->dev_repr);
|
|
|
+
|
|
|
+ uint8_t *jobrsp = &buf[MINERGATE_PKT_HEADER_SZ];
|
|
|
+ struct work *work;
|
|
|
+ uint16_t rsp_count = upk_u16le(buf, 6);
|
|
|
+ if (rsp_count || opt_dev_protocol)
|
|
|
+ applog(LOG_DEBUG, "%s: Received %u job completions", dev->dev_repr, rsp_count);
|
|
|
+ uint32_t nonce;
|
|
|
+ for (unsigned i = 0; i < rsp_count; ++i, (jobrsp += MINERGATE_PKT_RSP_ITEM_SZ))
|
|
|
+ {
|
|
|
+ work_device_id_t jobid = upk_u32be(jobrsp, 0);
|
|
|
+ nonce = upk_u32le(jobrsp, 8);
|
|
|
+ HASH_FIND(hh, thr->work, &jobid, sizeof(jobid), work);
|
|
|
+ if (!work)
|
|
|
+ {
|
|
|
+ applog(LOG_ERR, "%s: Unknown job %"PRIwdi, dev->dev_repr, jobid);
|
|
|
+ if (nonce)
|
|
|
+ {
|
|
|
+ inc_hw_errors3(thr, NULL, &nonce, 1.);
|
|
|
+ nonce = upk_u32le(jobrsp, 0xc);
|
|
|
+ if (nonce)
|
|
|
+ inc_hw_errors3(thr, NULL, &nonce, 1.);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ inc_hw_errors_only(thr);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (nonce)
|
|
|
+ {
|
|
|
+ submit_nonce(thr, work, nonce);
|
|
|
+
|
|
|
+ nonce = upk_u32be(jobrsp, 0xc);
|
|
|
+ if (nonce)
|
|
|
+ submit_nonce(thr, work, nonce);
|
|
|
+ }
|
|
|
+
|
|
|
+ HASH_DEL(thr->work, work);
|
|
|
+ free_work(work);
|
|
|
+ }
|
|
|
+
|
|
|
+ minergate_queue_full(thr);
|
|
|
+ timer_set_delay_from_now(&thr->tv_poll, MINERGATE_POLL_US);
|
|
|
+ return;
|
|
|
+
|
|
|
+err:
|
|
|
+ // TODO: reconnect
|
|
|
+ timer_set_delay_from_now(&thr->tv_poll, MINERGATE_RETRY_US);
|
|
|
+}
|
|
|
+
|
|
|
struct device_drv minergate_drv = {
|
|
|
.dname = "minergate",
|
|
|
.name = "MGT",
|
|
|
.drv_detect = minergate_detect,
|
|
|
+
|
|
|
+ .thread_init = minergate_init,
|
|
|
+ .minerloop = minerloop_queue,
|
|
|
+
|
|
|
+ .queue_append = minergate_queue_append,
|
|
|
+ .queue_flush = minergate_queue_flush,
|
|
|
+ .poll = minergate_poll,
|
|
|
};
|