driver-rockminer.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /*
  2. * Copyright 2014 Luke Dashjr
  3. * Copyright 2014 Nate Woolls
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the Free
  7. * Software Foundation; either version 3 of the License, or (at your option)
  8. * any later version. See COPYING for more details.
  9. */
  10. #include "config.h"
  11. #include <stdbool.h>
  12. #include <stdint.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include "deviceapi.h"
  16. #include "lowlevel.h"
  17. #include "lowl-vcom.h"
  18. #include "miner.h"
  19. #define ROCKMINER_MIN_FREQ_MHZ 200
  20. #define ROCKMINER_DEF_FREQ_MHZ 270
  21. #define ROCKMINER_MAX_SAFE_FREQ_MHZ 290
  22. #define ROCKMINER_MAX_FREQ_MHZ 640
  23. #define ROCKMINER_POLL_US 0
  24. #define ROCKMINER_RETRY_US 5000000
  25. #define ROCKMINER_MIDTASK_TIMEOUT_US 500000
  26. #define ROCKMINER_MIDTASK_RETRY_US 1000000
  27. #define ROCKMINER_TASK_TIMEOUT_US 5273438
  28. #define ROCKMINER_IO_SPEED 115200
  29. #define ROCKMINER_READ_TIMEOUT 1 //deciseconds
  30. #define ROCKMINER_MAX_CHIPS 64
  31. #define ROCKMINER_WORK_REQ_SIZE 0x40
  32. #define ROCKMINER_REPLY_SIZE 8
  33. enum rockminer_replies {
  34. ROCKMINER_REPLY_NONCE_FOUND = 0,
  35. ROCKMINER_REPLY_TASK_COMPLETE = 1,
  36. ROCKMINER_REPLY_GET_TASK = 2,
  37. };
  38. BFG_REGISTER_DRIVER(rockminer_drv)
  39. static const struct bfg_set_device_definition rockminer_set_device_funcs[];
  40. struct rockminer_chip_data {
  41. uint8_t next_work_req[ROCKMINER_WORK_REQ_SIZE];
  42. struct work *works[2];
  43. uint8_t last_taskid;
  44. struct timeval tv_midtask_timeout;
  45. int requested_work;
  46. };
  47. static
  48. int rockminer_open(const char *devpath)
  49. {
  50. return serial_open(devpath, ROCKMINER_IO_SPEED, ROCKMINER_READ_TIMEOUT, true);
  51. }
  52. static
  53. void rockminer_log_protocol(int fd, const void *buf, size_t bufLen, const char *prefix)
  54. {
  55. char hex[(bufLen * 2) + 1];
  56. bin2hex(hex, buf, bufLen);
  57. applog(LOG_DEBUG, "%s fd=%d: DEVPROTO: %s %s", rockminer_drv.dname, fd, prefix, hex);
  58. }
  59. static
  60. int rockminer_read(int fd, void *buf, size_t bufLen)
  61. {
  62. int result = read(fd, buf, bufLen);
  63. if (result < 0)
  64. applog(LOG_ERR, "%s: %s fd %d", rockminer_drv.dname, "Failed to read", fd);
  65. else if ((result > 0) && opt_dev_protocol && opt_debug)
  66. rockminer_log_protocol(fd, buf, bufLen, "RECV");
  67. return result;
  68. }
  69. static
  70. int rockminer_write(int fd, const void *buf, size_t bufLen)
  71. {
  72. if (opt_dev_protocol && opt_debug)
  73. rockminer_log_protocol(fd, buf, bufLen, "SEND");
  74. return write(fd, buf, bufLen);
  75. }
  76. static
  77. void rockminer_job_buf_init(uint8_t * const buf, const uint8_t chipid)
  78. {
  79. memset(&buf[0x20], 0, 0x10);
  80. buf[0x30] = 0xaa;
  81. // 0x31 is frequency, filled in elsewhere
  82. buf[0x32] = chipid;
  83. buf[0x33] = 0x55;
  84. }
  85. static
  86. void rockminer_job_buf_set_freq(uint8_t * const buf, const unsigned short freq)
  87. {
  88. buf[0x31] = (freq / 10) - 1;
  89. }
  90. static
  91. bool rockminer_lowl_match(const struct lowlevel_device_info * const info)
  92. {
  93. return lowlevel_match_product(info, "R-BOX miner") || lowlevel_match_product(info, "RX-BOX miner");
  94. }
  95. static const uint8_t golden_midstate[] = {
  96. 0x4a, 0x54, 0x8f, 0xe4, 0x71, 0xfa, 0x3a, 0x9a,
  97. 0x13, 0x71, 0x14, 0x45, 0x56, 0xc3, 0xf6, 0x4d,
  98. 0x25, 0x00, 0xb4, 0x82, 0x60, 0x08, 0xfe, 0x4b,
  99. 0xbf, 0x76, 0x98, 0xc9, 0x4e, 0xba, 0x79, 0x46,
  100. };
  101. static const uint8_t golden_datatail[] = {
  102. 0xce, 0x22, 0xa7, 0x2f,
  103. 0x4f, 0x67, 0x26, 0x14, 0x1a, 0x0b, 0x32, 0x87,
  104. };
  105. static const uint8_t golden_result[] = {
  106. 0x00, 0x01, 0x87, 0xa2,
  107. };
  108. int8_t rockminer_bisect_chips(const int fd, uint8_t * const buf)
  109. {
  110. static const int max_concurrent_tests = 4;
  111. int concurrent_tests = max_concurrent_tests;
  112. uint8_t tests[max_concurrent_tests];
  113. uint8_t reply[ROCKMINER_REPLY_SIZE];
  114. uint8_t minvalid = 0, maxvalid = ROCKMINER_MAX_CHIPS - 1;
  115. uint8_t pertest;
  116. char msg[0x10];
  117. ssize_t rsz;
  118. do {
  119. pertest = (maxvalid + 1 - minvalid) / concurrent_tests;
  120. if (!pertest)
  121. pertest = 1;
  122. msg[0] = '\0';
  123. for (int i = 0; i < concurrent_tests; ++i)
  124. {
  125. uint8_t chipid = (minvalid + pertest * (i + 1)) - 1;
  126. if (chipid > maxvalid)
  127. {
  128. concurrent_tests = i;
  129. break;
  130. }
  131. tests[i] = chipid;
  132. buf[0x32] = chipid;
  133. if (rockminer_write(fd, buf, ROCKMINER_WORK_REQ_SIZE) != ROCKMINER_WORK_REQ_SIZE)
  134. applogr(-1, LOG_DEBUG, "%s(%d): Error sending request for chip %d", __func__, fd, chipid);
  135. tailsprintf(msg, sizeof(msg), "%d ", chipid);
  136. }
  137. msg[strlen(msg)-1] = '\0';
  138. applog(LOG_DEBUG, "%s(%d): Testing chips %s (within range %d-%d)", __func__, fd, msg, minvalid, maxvalid);
  139. while ( (rsz = rockminer_read(fd, reply, sizeof(reply))) == sizeof(reply))
  140. {
  141. const uint8_t chipid = reply[5] & 0x3f;
  142. if (chipid > minvalid)
  143. {
  144. applog(LOG_DEBUG, "%s(%d): Saw chip %d", __func__, fd, chipid);
  145. minvalid = chipid;
  146. if (minvalid >= tests[concurrent_tests-1])
  147. break;
  148. }
  149. }
  150. for (int i = concurrent_tests; i--; )
  151. {
  152. if (tests[i] > minvalid)
  153. {
  154. applog(LOG_DEBUG, "%s(%d): Didn't see chip %d", __func__, fd, tests[i]);
  155. maxvalid = tests[i] - 1;
  156. }
  157. else
  158. break;
  159. }
  160. } while (minvalid != maxvalid);
  161. return maxvalid + 1;
  162. }
  163. static
  164. bool rockminer_detect_one(const char * const devpath)
  165. {
  166. int fd, chips;
  167. uint8_t buf[ROCKMINER_WORK_REQ_SIZE], reply[ROCKMINER_REPLY_SIZE];
  168. ssize_t rsz;
  169. fd = rockminer_open(devpath);
  170. if (fd < 0)
  171. return_via_applog(err, , LOG_DEBUG, "%s: %s %s", rockminer_drv.dname, "Failed to open", devpath);
  172. applog(LOG_DEBUG, "%s: %s %s", rockminer_drv.dname, "Successfully opened", devpath);
  173. rockminer_job_buf_init(buf, 0);
  174. rockminer_job_buf_set_freq(buf, ROCKMINER_MIN_FREQ_MHZ);
  175. memcpy(&buf[ 0], golden_midstate, 0x20);
  176. memcpy(&buf[0x34], golden_datatail, 0xc);
  177. if (rockminer_write(fd, buf, sizeof(buf)) != sizeof(buf))
  178. return_via_applog(err, , LOG_DEBUG, "%s: %s %s", rockminer_drv.dname, "Error sending request to ", devpath);
  179. while (true)
  180. {
  181. rsz = rockminer_read(fd, reply, sizeof(reply));
  182. if (rsz != sizeof(reply))
  183. return_via_applog(err, , LOG_DEBUG, "%s: Short read from %s (%d)", rockminer_drv.dname, devpath, rsz);
  184. if ((!memcmp(reply, golden_result, sizeof(golden_result))) && (reply[4] & 0xf) == ROCKMINER_REPLY_NONCE_FOUND)
  185. break;
  186. }
  187. applog(LOG_DEBUG, "%s: Found chip 0 on %s, probing for total chip count", rockminer_drv.dname, devpath);
  188. chips = rockminer_bisect_chips(fd, buf);
  189. applog(LOG_DEBUG, "%s: Identified %d chips on %s", rockminer_drv.dname, chips, devpath);
  190. if (serial_claim_v(devpath, &rockminer_drv))
  191. goto err;
  192. serial_close(fd);
  193. struct cgpu_info * const cgpu = malloc(sizeof(*cgpu));
  194. *cgpu = (struct cgpu_info){
  195. .drv = &rockminer_drv,
  196. .set_device_funcs = rockminer_set_device_funcs,
  197. .device_path = strdup(devpath),
  198. .deven = DEV_ENABLED,
  199. .procs = chips,
  200. .threads = 1,
  201. };
  202. // NOTE: Xcode's clang has a bug where it cannot find fields inside anonymous unions (more details in fpgautils)
  203. cgpu->device_fd = -1;
  204. return add_cgpu(cgpu);
  205. err:
  206. if (fd >= 0)
  207. serial_close(fd);
  208. return false;
  209. }
  210. static
  211. bool rockminer_lowl_probe(const struct lowlevel_device_info * const info)
  212. {
  213. return vcom_lowl_probe_wrapper(info, rockminer_detect_one);
  214. }
  215. static
  216. bool rockminer_init(struct thr_info * const master_thr)
  217. {
  218. struct cgpu_info * const dev = master_thr->cgpu;
  219. for_each_managed_proc(proc, dev)
  220. {
  221. struct thr_info * const thr = proc->thr[0];
  222. struct rockminer_chip_data * const chip = malloc(sizeof(*chip));
  223. thr->cgpu_data = chip;
  224. *chip = (struct rockminer_chip_data){
  225. .last_taskid = 0,
  226. };
  227. rockminer_job_buf_init(chip->next_work_req, proc->proc_id);
  228. rockminer_job_buf_set_freq(chip->next_work_req, ROCKMINER_DEF_FREQ_MHZ);
  229. }
  230. timer_set_now(&master_thr->tv_poll);
  231. return true;
  232. }
  233. static
  234. void rockminer_dead(struct cgpu_info * const dev)
  235. {
  236. serial_close(dev->device_fd);
  237. dev->device_fd = -1;
  238. for_each_managed_proc(proc, dev)
  239. {
  240. struct thr_info * const thr = proc->thr[0];
  241. thr->queue_full = true;
  242. }
  243. }
  244. static
  245. bool rockminer_send_work(struct thr_info * const thr)
  246. {
  247. struct cgpu_info * const proc = thr->cgpu;
  248. struct cgpu_info * const dev = proc->device;
  249. struct rockminer_chip_data * const chip = thr->cgpu_data;
  250. const int fd = dev->device_fd;
  251. return (rockminer_write(fd, chip->next_work_req, sizeof(chip->next_work_req)) == sizeof(chip->next_work_req));
  252. }
  253. static
  254. bool rockminer_queue_append(struct thr_info * const thr, struct work * const work)
  255. {
  256. struct cgpu_info * const proc = thr->cgpu;
  257. struct cgpu_info * const dev = proc->device;
  258. struct rockminer_chip_data * const chip = thr->cgpu_data;
  259. const int fd = dev->device_fd;
  260. if (fd < 0 || !chip->requested_work)
  261. {
  262. thr->queue_full = true;
  263. return false;
  264. }
  265. memcpy(&chip->next_work_req[ 0], work->midstate, 0x20);
  266. memcpy(&chip->next_work_req[0x34], &work->data[0x40], 0xc);
  267. if (!rockminer_send_work(thr))
  268. {
  269. rockminer_dead(dev);
  270. inc_hw_errors_only(thr);
  271. applogr(false, LOG_ERR, "%"PRIpreprv": Failed to send work", proc->proc_repr);
  272. }
  273. chip->last_taskid = chip->last_taskid ? 0 : 1;
  274. if (chip->works[chip->last_taskid])
  275. free_work(chip->works[chip->last_taskid]);
  276. chip->works[chip->last_taskid] = work;
  277. timer_set_delay_from_now(&chip->tv_midtask_timeout, ROCKMINER_MIDTASK_RETRY_US);
  278. applog(LOG_DEBUG, "%"PRIpreprv": Work %d queued as task %d", proc->proc_repr, work->id, chip->last_taskid);
  279. if (!--chip->requested_work)
  280. thr->queue_full = true;
  281. return true;
  282. }
  283. static
  284. void rockminer_queue_flush(__maybe_unused struct thr_info * const thr)
  285. {
  286. }
  287. static
  288. void rockminer_poll(struct thr_info * const master_thr)
  289. {
  290. struct cgpu_info * const dev = master_thr->cgpu;
  291. int fd = dev->device_fd;
  292. uint8_t reply[ROCKMINER_REPLY_SIZE];
  293. ssize_t rsz;
  294. if (fd < 0)
  295. {
  296. fd = rockminer_open(dev->device_path);
  297. if (fd < 0)
  298. {
  299. timer_set_delay_from_now(&master_thr->tv_poll, ROCKMINER_RETRY_US);
  300. for_each_managed_proc(proc, dev)
  301. {
  302. struct thr_info * const thr = proc->thr[0];
  303. inc_hw_errors_only(thr);
  304. }
  305. applogr(, LOG_ERR, "%s: Failed to open %s", dev->dev_repr, dev->device_path);
  306. }
  307. dev->device_fd = fd;
  308. struct timeval tv_timeout;
  309. timer_set_delay_from_now(&tv_timeout, ROCKMINER_TASK_TIMEOUT_US);
  310. for_each_managed_proc(proc, dev)
  311. {
  312. struct thr_info * const thr = proc->thr[0];
  313. struct rockminer_chip_data * const chip = thr->cgpu_data;
  314. chip->requested_work = 1;
  315. thr->queue_full = false;
  316. chip->tv_midtask_timeout = tv_timeout;
  317. }
  318. }
  319. while ( (rsz = rockminer_read(fd, reply, sizeof(reply))) == sizeof(reply))
  320. {
  321. // const uint8_t status = reply[4] >> 4;
  322. const enum rockminer_replies cmd = reply[4] & 0xf;
  323. // const uint8_t prodid = reply[5] >> 6;
  324. const uint8_t chipid = reply[5] & 0x3f;
  325. const uint8_t taskid = reply[6] & 1;
  326. const uint8_t temp = reply[7];
  327. struct cgpu_info * const proc = device_proc_by_id(dev, chipid);
  328. if (unlikely(!proc))
  329. {
  330. for_each_managed_proc(proc, dev)
  331. {
  332. struct thr_info * const thr = proc->thr[0];
  333. inc_hw_errors_only(thr);
  334. }
  335. applog(LOG_ERR, "%s: Chip id %d out of range", dev->dev_repr, chipid);
  336. continue;
  337. }
  338. struct thr_info * const thr = proc->thr[0];
  339. struct rockminer_chip_data * const chip = thr->cgpu_data;
  340. if (temp != 128)
  341. proc->temp = temp;
  342. switch (cmd) {
  343. case ROCKMINER_REPLY_NONCE_FOUND:
  344. {
  345. const uint32_t nonce = upk_u32be(reply, 0);
  346. struct work *work;
  347. if (chip->works[taskid] && test_nonce(chip->works[taskid], nonce, false))
  348. {}
  349. else
  350. if (chip->works[taskid ? 0 : 1] && test_nonce(chip->works[taskid ? 0 : 1], nonce, false))
  351. {
  352. applog(LOG_DEBUG, "%"PRIpreprv": We have task ids inverted; fixing", proc->proc_repr);
  353. work = chip->works[0];
  354. chip->works[0] = chip->works[1];
  355. chip->works[1] = work;
  356. chip->last_taskid = chip->last_taskid ? 0 : 1;
  357. }
  358. work = chip->works[taskid];
  359. submit_nonce(thr, work, nonce);
  360. break;
  361. }
  362. case ROCKMINER_REPLY_TASK_COMPLETE:
  363. applog(LOG_DEBUG, "%"PRIpreprv": Task %d completed", proc->proc_repr, taskid);
  364. hashes_done2(thr, 0x100000000, NULL);
  365. if (proc->deven == DEV_ENABLED)
  366. timer_set_delay_from_now(&chip->tv_midtask_timeout, ROCKMINER_MIDTASK_TIMEOUT_US);
  367. break;
  368. case ROCKMINER_REPLY_GET_TASK:
  369. applog(LOG_DEBUG, "%"PRIpreprv": Task %d requested", proc->proc_repr, taskid);
  370. thr->queue_full = false;
  371. ++chip->requested_work;
  372. if (proc->deven == DEV_ENABLED)
  373. timer_set_delay_from_now(&chip->tv_midtask_timeout, ROCKMINER_TASK_TIMEOUT_US);
  374. break;
  375. }
  376. }
  377. if (rsz < 0)
  378. rockminer_dead(dev);
  379. struct timeval tv_now;
  380. timer_set_now(&tv_now);
  381. for_each_managed_proc(proc, dev)
  382. {
  383. struct thr_info * const thr = proc->thr[0];
  384. struct rockminer_chip_data * const chip = thr->cgpu_data;
  385. if (timer_passed(&chip->tv_midtask_timeout, &tv_now))
  386. {
  387. if (proc->deven != DEV_ENABLED)
  388. {
  389. timer_unset(&chip->tv_midtask_timeout);
  390. continue;
  391. }
  392. // A task completed, but no request followed
  393. // This means it missed our last task send, so we need to resend it
  394. applog(LOG_WARNING, "%"PRIpreprv": No task request? Probably lost, resending task %d", proc->proc_repr, chip->last_taskid);
  395. inc_hw_errors_only(thr);
  396. timer_set_delay(&chip->tv_midtask_timeout, &tv_now, ROCKMINER_MIDTASK_RETRY_US);
  397. struct work *work;
  398. if ((!(work = chip->works[chip->last_taskid])) || stale_work(work, false))
  399. {
  400. // Either no work was queued, or it was stale
  401. // Instead of resending, just queue a new one
  402. if (!chip->requested_work)
  403. chip->requested_work = 1;
  404. thr->queue_full = false;
  405. }
  406. else
  407. if (!rockminer_send_work(thr))
  408. {
  409. rockminer_dead(dev);
  410. timer_set_delay_from_now(&master_thr->tv_poll, ROCKMINER_RETRY_US);
  411. inc_hw_errors_only(thr);
  412. applogr(, LOG_ERR, "%"PRIpreprv": Failed to resend work", proc->proc_repr);
  413. }
  414. }
  415. }
  416. timer_set_delay_from_now(&master_thr->tv_poll, ROCKMINER_POLL_US);
  417. }
  418. static
  419. const char *rockminer_set_clock(struct cgpu_info * const proc, const char * const optname, const char *newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
  420. {
  421. struct thr_info * const thr = proc->thr[0];
  422. struct rockminer_chip_data * const chip = thr->cgpu_data;
  423. bool unsafe = false;
  424. if (!strncasecmp(newvalue, "unsafe:", 7))
  425. {
  426. newvalue += 7;
  427. unsafe = true;
  428. }
  429. const int val = atoi(newvalue);
  430. if (val < ROCKMINER_MIN_FREQ_MHZ || val > ROCKMINER_MAX_FREQ_MHZ)
  431. return "Invalid clock speed";
  432. else
  433. if (val > ROCKMINER_MAX_SAFE_FREQ_MHZ && !unsafe)
  434. return "Dangerous clock speed (use \"unsafe:N\" to force)";
  435. applog(LOG_DEBUG, "%"PRIpreprv": Changing clock frequency for future jobs to %d MHz", proc->proc_repr, val);
  436. rockminer_job_buf_set_freq(chip->next_work_req, val);
  437. return NULL;
  438. }
  439. static const struct bfg_set_device_definition rockminer_set_device_funcs[] = {
  440. {"clock", rockminer_set_clock, "clock frequency"},
  441. {NULL}
  442. };
  443. static
  444. int rockminer_get_clock(struct cgpu_info * const proc)
  445. {
  446. struct thr_info * const thr = proc->thr[0];
  447. struct rockminer_chip_data * const chip = thr->cgpu_data;
  448. return ((int)chip->next_work_req[0x31] + 1) * 10;
  449. }
  450. static
  451. struct api_data *rockminer_get_extra_device_status(struct cgpu_info * const proc)
  452. {
  453. struct api_data *root = NULL;
  454. double d = rockminer_get_clock(proc);
  455. root = api_add_freq(root, "Frequency", &d, true);
  456. return root;
  457. }
  458. #ifdef HAVE_CURSES
  459. static
  460. void rockminer_tui_wlogprint_choices(struct cgpu_info * const proc)
  461. {
  462. wlogprint("[C]lock speed ");
  463. }
  464. static
  465. const char *rockminer_tui_handle_choice(struct cgpu_info * const proc, const int input)
  466. {
  467. static char buf[0x100]; // Static for replies
  468. switch (input)
  469. {
  470. case 'c': case 'C':
  471. {
  472. sprintf(buf, "Set clock speed (range %d-%d, multiple of 10)", ROCKMINER_MIN_FREQ_MHZ, ROCKMINER_MAX_FREQ_MHZ);
  473. char * const val = curses_input(buf);
  474. const char * const msg = rockminer_set_clock(proc, "clock", val ?: "", NULL, NULL);
  475. free(val);
  476. if (msg)
  477. {
  478. snprintf(buf, sizeof(buf), "%s\n", msg);
  479. return buf;
  480. }
  481. return "Clock speed changed\n";
  482. }
  483. }
  484. return NULL;
  485. }
  486. static
  487. void rockminer_wlogprint_status(struct cgpu_info * const proc)
  488. {
  489. wlogprint("Clock speed: %d\n", rockminer_get_clock(proc));
  490. }
  491. #endif
  492. struct device_drv rockminer_drv = {
  493. .dname = "rockminer",
  494. .name = "RKM",
  495. .lowl_match = rockminer_lowl_match,
  496. .lowl_probe = rockminer_lowl_probe,
  497. .thread_init = rockminer_init,
  498. .minerloop = minerloop_queue,
  499. .queue_append = rockminer_queue_append,
  500. .queue_flush = rockminer_queue_flush,
  501. .poll = rockminer_poll,
  502. .get_api_extra_device_status = rockminer_get_extra_device_status,
  503. #ifdef HAVE_CURSES
  504. .proc_wlogprint_status = rockminer_wlogprint_status,
  505. .proc_tui_wlogprint_choices = rockminer_tui_wlogprint_choices,
  506. .proc_tui_handle_choice = rockminer_tui_handle_choice,
  507. #endif
  508. };