Browse Source

Merge branch 'rockminer' into bfgminer

Luke Dashjr 11 years ago
parent
commit
7afc7e81f4
7 changed files with 553 additions and 13 deletions
  1. 2 0
      70-bfgminer.rules.in
  2. 4 0
      Makefile.am
  3. 15 0
      configure.ac
  4. 13 0
      deviceapi.c
  5. 1 0
      deviceapi.h
  6. 0 13
      driver-bifury.c
  7. 518 0
      driver-rockminer.c

+ 2 - 0
70-bfgminer.rules.in

@@ -19,6 +19,8 @@ LABEL="bfgminer_start"
 @HAS_LITTLEFURY_TRUE@ENV{ID_MODEL}=="*LittleFury*", GOTO="bfgminer_add"
 @HAS_MODMINER_TRUE@ENV{ID_MODEL}=="*ModMiner*", GOTO="bfgminer_add"
 @HAS_NANOFURY_TRUE@ENV{idVendor}=="04d8", ENV{idProduct}=="00de", ENV{ID_MODEL}=="*NanoFury*", GOTO="bfgminer_add"
+@USE_ROCKMINER_TRUE@ENV{ID_MODEL}=="*R-BOX miner*", GOTO="bfgminer_add"
+@USE_ROCKMINER_TRUE@ENV{ID_MODEL}=="*RX-BOX miner*", GOTO="bfgminer_add"
 @HAS_TWINFURY_TRUE@ENV{ID_MODEL}=="*Twinfury*", GOTO="bfgminer_add"
 @HAS_X6500_TRUE@ENV{idVendor}=="0403", ENV{idProduct}=="6001", ENV{ID_MODEL}=="*X6500 FPGA Miner*", GOTO="bfgminer_add"
 @HAS_ZTEX_TRUE@ENV{ID_MODEL}=="*btcminer for ZTEX*", GOTO="bfgminer_add"

+ 4 - 0
Makefile.am

@@ -320,6 +320,10 @@ if USE_HASHFAST
 bfgminer_SOURCES += driver-hashfast.c
 endif
 
+if USE_ROCKMINER
+bfgminer_SOURCES += driver-rockminer.c
+endif
+
 if NEED_BFG_LOWL_FTDI
 bfgminer_SOURCES += lowl-ftdi.c lowl-ftdi.h
 endif

+ 15 - 0
configure.ac

@@ -1036,6 +1036,21 @@ fi
 AM_CONDITIONAL([HAS_METABANK], [test x$metabank = xyes])
 
 
+driverlist="$driverlist rockminer"
+AC_ARG_ENABLE([rockminer],
+	[AC_HELP_STRING([--disable-rockminer],[Compile support for RockMiner (default enabled)])],
+	[rockminer=$enableval],
+	[rockminer=$ddyes]
+)
+if test "x$rockminer" = xyes; then
+	AC_DEFINE([USE_ROCKMINER], [1], [Defined to 1 if RockMiner support is wanted])
+	need_lowl_vcom=yes
+	has_asic=yes
+	have_udevrules=true
+fi
+AM_CONDITIONAL([USE_ROCKMINER], [test x$rockminer = xyes])
+
+
 if test "x$need_lowl_vcom" != "xno"; then
 	# Lowlevel VCOM doesn't need libusb, but it can take advantage of it to reattach drivers
 	if test "x$libusb" != xno; then

+ 13 - 0
deviceapi.c

@@ -1083,3 +1083,16 @@ void close_device_fd(struct thr_info * const thr)
 		applog(LOG_DEBUG, "%"PRIpreprv": Closed device fd", proc->proc_repr);
 	}
 }
+
+
+struct cgpu_info *device_proc_by_id(struct cgpu_info * const dev, const int procid)
+{
+	struct cgpu_info *proc = dev;
+	for (int i = 0; i < procid; ++i)
+	{
+		proc = proc->next_proc;
+		if (unlikely((!proc) || proc->device != dev))
+			return NULL;
+	}
+	return proc;
+}

+ 1 - 0
deviceapi.h

@@ -114,5 +114,6 @@ extern void close_device_fd(struct thr_info *);
 	for (struct cgpu_info *procvar = dev; procvar; procvar = procvar->next_proc)
 #define for_each_logical_proc(procvar, dev)  \
 	for (struct cgpu_info *procvar = dev; procvar->proc_id < dev->procs; procvar = procvar->next_proc)
+extern struct cgpu_info *device_proc_by_id(struct cgpu_info *dev, int procid);
 
 #endif

+ 0 - 13
driver-bifury.c

@@ -363,19 +363,6 @@ void bifury_queue_flush(struct thr_info * const thr)
 	bifury_set_queue_full(dev, dev->procs);
 }
 
-static
-const struct cgpu_info *device_proc_by_id(const struct cgpu_info * const dev, int procid)
-{
-	const struct cgpu_info *proc = dev;
-	for (int i = 0; i < procid; ++i)
-	{
-		proc = proc->next_proc;
-		if (unlikely(!proc))
-			return NULL;
-	}
-	return proc;
-}
-
 static
 void bifury_handle_cmd(struct cgpu_info * const dev, const char * const cmd)
 {

+ 518 - 0
driver-rockminer.c

@@ -0,0 +1,518 @@
+/*
+ * 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 "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "deviceapi.h"
+#include "lowlevel.h"
+#include "lowl-vcom.h"
+#include "miner.h"
+
+#define ROCKMINER_MIN_FREQ_MHZ  200
+#define ROCKMINER_DEF_FREQ_MHZ  270
+#define ROCKMINER_MAX_FREQ_MHZ  290
+#define ROCKMINER_POLL_US         0
+#define ROCKMINER_RETRY_US  5000000
+#define ROCKMINER_MIDTASK_TIMEOUT_US  500000
+#define ROCKMINER_MIDTASK_RETRY_US   1000000
+
+#define ROCKMINER_MAX_CHIPS  64
+#define ROCKMINER_WORK_REQ_SIZE  0x40
+#define ROCKMINER_REPLY_SIZE        8
+
+enum rockminer_replies {
+	ROCKMINER_REPLY_NONCE_FOUND = 0,
+	ROCKMINER_REPLY_TASK_COMPLETE = 1,
+	ROCKMINER_REPLY_GET_TASK = 2,
+};
+
+BFG_REGISTER_DRIVER(rockminer_drv)
+static const struct bfg_set_device_definition rockminer_set_device_funcs[];
+
+struct rockminer_chip_data {
+	uint8_t next_work_req[ROCKMINER_WORK_REQ_SIZE];
+	struct work *works[2];
+	uint8_t last_taskid;
+	struct timeval tv_midtask_timeout;
+};
+
+static
+void rockminer_job_buf_init(uint8_t * const buf, const uint8_t chipid)
+{
+	memset(&buf[0x20], 0, 0x10);
+	buf[0x30] = 0xaa;
+	// 0x31 is frequency, filled in elsewhere
+	buf[0x32] = chipid;
+	buf[0x33] = 0x55;
+}
+
+static
+void rockminer_job_buf_set_freq(uint8_t * const buf, const unsigned short freq)
+{
+	buf[0x31] = (freq / 10) - 1;
+}
+
+static
+bool rockminer_lowl_match(const struct lowlevel_device_info * const info)
+{
+	return lowlevel_match_product(info, "R-BOX miner") || lowlevel_match_product(info, "RX-BOX miner");
+}
+
+static const uint8_t golden_midstate[] = {
+	0x4a, 0x54, 0x8f, 0xe4, 0x71, 0xfa, 0x3a, 0x9a,
+	0x13, 0x71, 0x14, 0x45, 0x56, 0xc3, 0xf6, 0x4d,
+	0x25, 0x00, 0xb4, 0x82, 0x60, 0x08, 0xfe, 0x4b,
+	0xbf, 0x76, 0x98, 0xc9, 0x4e, 0xba, 0x79, 0x46,
+};
+
+static const uint8_t golden_datatail[] = {
+	                        0xce, 0x22, 0xa7, 0x2f,
+	0x4f, 0x67, 0x26, 0x14, 0x1a, 0x0b, 0x32, 0x87,
+};
+
+static const uint8_t golden_result[] = {
+	0x00, 0x01, 0x87, 0xa2,
+};
+
+int8_t rockminer_bisect_chips(const int fd, uint8_t * const buf)
+{
+	static const int max_concurrent_tests = 4;
+	int concurrent_tests = max_concurrent_tests;
+	uint8_t tests[max_concurrent_tests];
+	uint8_t reply[ROCKMINER_REPLY_SIZE];
+	uint8_t minvalid = 0, maxvalid = ROCKMINER_MAX_CHIPS - 1;
+	uint8_t pertest;
+	char msg[0x10];
+	ssize_t rsz;
+	
+	do {
+		pertest = (maxvalid + 1 - minvalid) / concurrent_tests;
+		if (!pertest)
+			pertest = 1;
+		msg[0] = '\0';
+		for (int i = 0; i < concurrent_tests; ++i)
+		{
+			uint8_t chipid = (minvalid + pertest * (i + 1)) - 1;
+			if (chipid > maxvalid)
+			{
+				concurrent_tests = i;
+				break;
+			}
+			tests[i] = chipid;
+			
+			buf[0x32] = chipid;
+			if (write(fd, buf, ROCKMINER_WORK_REQ_SIZE) != ROCKMINER_WORK_REQ_SIZE)
+				applogr(-1, LOG_DEBUG, "%s(%d): Error sending request for chip %d", __func__, fd, chipid);
+			
+			tailsprintf(msg, sizeof(msg), "%d ", chipid);
+		}
+		
+		msg[strlen(msg)-1] = '\0';
+		applog(LOG_DEBUG, "%s(%d): Testing chips %s (within range %d-%d)", __func__, fd, msg, minvalid, maxvalid);
+		
+		while ( (rsz = read(fd, reply, sizeof(reply))) == sizeof(reply))
+		{
+			const uint8_t chipid = reply[5] & 0x3f;
+			if (chipid > minvalid)
+			{
+				applog(LOG_DEBUG, "%s(%d): Saw chip %d", __func__, fd, chipid);
+				minvalid = chipid;
+				if (minvalid >= tests[concurrent_tests-1])
+					break;
+			}
+		}
+		
+		for (int i = concurrent_tests; i--; )
+		{
+			if (tests[i] > minvalid)
+			{
+				applog(LOG_DEBUG, "%s(%d): Didn't see chip %d", __func__, fd, tests[i]);
+				maxvalid = tests[i] - 1;
+			}
+			else
+				break;
+		}
+	} while (minvalid != maxvalid);
+	
+	return maxvalid + 1;
+}
+
+static
+bool rockminer_detect_one(const char * const devpath)
+{
+	int fd, chips;
+	uint8_t buf[ROCKMINER_WORK_REQ_SIZE], reply[ROCKMINER_REPLY_SIZE];
+	ssize_t rsz;
+	
+	fd = serial_open(devpath, 0, 1, true);
+	if (fd < 0)
+		return_via_applog(err, , LOG_DEBUG, "%s: %s %s", rockminer_drv.dname, "Failed to open", devpath);
+	
+	applog(LOG_DEBUG, "%s: %s %s", rockminer_drv.dname, "Successfully opened", devpath);
+	
+	rockminer_job_buf_init(buf, 0);
+	rockminer_job_buf_set_freq(buf, ROCKMINER_MIN_FREQ_MHZ);
+	memcpy(&buf[   0], golden_midstate, 0x20);
+	memcpy(&buf[0x34], golden_datatail,  0xc);
+	
+	if (write(fd, buf, sizeof(buf)) != sizeof(buf))
+		return_via_applog(err, , LOG_DEBUG, "%s: %s %s", rockminer_drv.dname, "Error sending request to ", devpath);
+	
+	while (true)
+	{
+		rsz = read(fd, reply, sizeof(reply));
+		if (rsz != sizeof(reply))
+			return_via_applog(err, , LOG_DEBUG, "%s: Short read from %s (%d)", rockminer_drv.dname, devpath, rsz);
+		if ((!memcmp(reply, golden_result, sizeof(golden_result))) && (reply[4] & 0xf) == ROCKMINER_REPLY_NONCE_FOUND)
+			break;
+	}
+	
+	applog(LOG_DEBUG, "%s: Found chip 0 on %s, probing for total chip count", rockminer_drv.dname, devpath);
+	chips = rockminer_bisect_chips(fd, buf);
+	applog(LOG_DEBUG, "%s: Identified %d chips on %s", rockminer_drv.dname, chips, devpath);
+	
+	if (serial_claim_v(devpath, &rockminer_drv))
+		goto err;
+	
+	serial_close(fd);
+	
+	struct cgpu_info * const cgpu = malloc(sizeof(*cgpu));
+	*cgpu = (struct cgpu_info){
+		.drv = &rockminer_drv,
+		.set_device_funcs = rockminer_set_device_funcs,
+		.device_path = strdup(devpath),
+		.deven = DEV_ENABLED,
+		.procs = chips,
+		.threads = 1,
+	};
+	// NOTE: Xcode's clang has a bug where it cannot find fields inside anonymous unions (more details in fpgautils)
+	cgpu->device_fd = -1;
+	
+	return add_cgpu(cgpu);
+
+err:
+	if (fd >= 0)
+		serial_close(fd);
+	return false;
+}
+
+static
+bool rockminer_lowl_probe(const struct lowlevel_device_info * const info)
+{
+	return vcom_lowl_probe_wrapper(info, rockminer_detect_one);
+}
+
+static
+bool rockminer_init(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const dev = master_thr->cgpu;
+	
+	for_each_managed_proc(proc, dev)
+	{
+		struct thr_info * const thr = proc->thr[0];
+		struct rockminer_chip_data * const chip = malloc(sizeof(*chip));
+		
+		thr->cgpu_data = chip;
+		*chip = (struct rockminer_chip_data){
+			.last_taskid = 0,
+		};
+		
+		rockminer_job_buf_init(chip->next_work_req, proc->proc_id);
+		rockminer_job_buf_set_freq(chip->next_work_req, ROCKMINER_DEF_FREQ_MHZ);
+	}
+	
+	timer_set_now(&master_thr->tv_poll);
+	
+	return true;
+}
+
+static
+void rockminer_dead(struct cgpu_info * const dev)
+{
+	serial_close(dev->device_fd);
+	dev->device_fd = -1;
+	for_each_managed_proc(proc, dev)
+	{
+		struct thr_info * const thr = proc->thr[0];
+		thr->queue_full = true;
+	}
+}
+
+static
+bool rockminer_send_work(struct thr_info * const thr)
+{
+	struct cgpu_info * const proc = thr->cgpu;
+	struct cgpu_info * const dev = proc->device;
+	struct rockminer_chip_data * const chip = thr->cgpu_data;
+	const int fd = dev->device_fd;
+	
+	return (write(fd, chip->next_work_req, sizeof(chip->next_work_req)) == sizeof(chip->next_work_req));
+}
+
+static
+bool rockminer_queue_append(struct thr_info * const thr, struct work * const work)
+{
+	struct cgpu_info * const proc = thr->cgpu;
+	struct cgpu_info * const dev = proc->device;
+	struct rockminer_chip_data * const chip = thr->cgpu_data;
+	const int fd = dev->device_fd;
+	
+	thr->queue_full = true;
+	
+	if (fd < 0)
+		return false;
+	
+	memcpy(&chip->next_work_req[   0], work->midstate, 0x20);
+	memcpy(&chip->next_work_req[0x34], &work->data[0x40], 0xc);
+	if (!rockminer_send_work(thr))
+	{
+		rockminer_dead(dev);
+		inc_hw_errors_only(thr);
+		applogr(false, LOG_ERR, "%"PRIpreprv": Failed to send work", proc->proc_repr);
+	}
+	
+	chip->last_taskid = chip->last_taskid ? 0 : 1;
+	if (chip->works[chip->last_taskid])
+		free_work(chip->works[chip->last_taskid]);
+	chip->works[chip->last_taskid] = work;
+	applog(LOG_DEBUG, "%"PRIpreprv": Work %d queued as task %d", proc->proc_repr, work->id, chip->last_taskid);
+	
+	return true;
+}
+
+static
+void rockminer_queue_flush(__maybe_unused struct thr_info * const thr)
+{
+	
+}
+
+static
+void rockminer_poll(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const dev = master_thr->cgpu;
+	int fd = dev->device_fd;
+	uint8_t reply[ROCKMINER_REPLY_SIZE];
+	ssize_t rsz;
+	
+	if (fd < 0)
+	{
+		fd = serial_open(dev->device_path, 0, 1, true);
+		if (fd < 0)
+		{
+			timer_set_delay_from_now(&master_thr->tv_poll, ROCKMINER_RETRY_US);
+			for_each_managed_proc(proc, dev)
+			{
+				struct thr_info * const thr = proc->thr[0];
+				inc_hw_errors_only(thr);
+			}
+			applogr(, LOG_ERR, "%s: Failed to open %s", dev->dev_repr, dev->device_path);
+		}
+		dev->device_fd = fd;
+		for_each_managed_proc(proc, dev)
+		{
+			struct thr_info * const thr = proc->thr[0];
+			struct rockminer_chip_data * const chip = thr->cgpu_data;
+			
+			thr->queue_full = false;
+			timer_unset(&chip->tv_midtask_timeout);
+		}
+	}
+	
+	while ( (rsz = read(fd, reply, sizeof(reply))) == sizeof(reply))
+	{
+// 		const uint8_t status = reply[4] >> 4;
+		const enum rockminer_replies cmd = reply[4] & 0xf;
+// 		const uint8_t prodid = reply[5] >> 6;
+		const uint8_t chipid = reply[5] & 0x3f;
+		const uint8_t taskid = reply[6] & 1;
+		const uint8_t temp = reply[7];
+		struct cgpu_info * const proc = device_proc_by_id(dev, chipid);
+		if (unlikely(!proc))
+		{
+			for_each_managed_proc(proc, dev)
+			{
+				struct thr_info * const thr = proc->thr[0];
+				inc_hw_errors_only(thr);
+			}
+			applog(LOG_ERR, "%s: Chip id %d out of range", dev->dev_repr, chipid);
+			continue;
+		}
+		struct thr_info * const thr = proc->thr[0];
+		struct rockminer_chip_data * const chip = thr->cgpu_data;
+		
+		if (temp != 128)
+			proc->temp = temp;
+		
+		switch (cmd) {
+			case ROCKMINER_REPLY_NONCE_FOUND:
+			{
+				const uint32_t nonce = upk_u32be(reply, 0);
+				struct work *work;
+				if (chip->works[taskid] && test_nonce(chip->works[taskid], nonce, false))
+				{}
+				else
+				if (chip->works[taskid ? 0 : 1] && test_nonce(chip->works[taskid ? 0 : 1], nonce, false))
+				{
+					applog(LOG_DEBUG, "%"PRIpreprv": We have task ids inverted; fixing", proc->proc_repr);
+					work = chip->works[0];
+					chip->works[0] = chip->works[1];
+					chip->works[1] = work;
+					chip->last_taskid = chip->last_taskid ? 0 : 1;
+				}
+				work = chip->works[taskid];
+				submit_nonce(thr, work, nonce);
+				break;
+			}
+			case ROCKMINER_REPLY_TASK_COMPLETE:
+				applog(LOG_DEBUG, "%"PRIpreprv": Task %d completed", proc->proc_repr, taskid);
+				hashes_done2(thr, 0x100000000, NULL);
+				timer_set_delay_from_now(&chip->tv_midtask_timeout, ROCKMINER_MIDTASK_TIMEOUT_US);
+				break;
+			case ROCKMINER_REPLY_GET_TASK:
+				applog(LOG_DEBUG, "%"PRIpreprv": Task %d requested", proc->proc_repr, taskid);
+				thr->queue_full = false;
+				timer_unset(&chip->tv_midtask_timeout);
+				break;
+		}
+	}
+	if (rsz < 0)
+		rockminer_dead(dev);
+	
+	struct timeval tv_now;
+	timer_set_now(&tv_now);
+	for_each_managed_proc(proc, dev)
+	{
+		struct thr_info * const thr = proc->thr[0];
+		struct rockminer_chip_data * const chip = thr->cgpu_data;
+		
+		if (timer_passed(&chip->tv_midtask_timeout, &tv_now))
+		{
+			// A task completed, but no request followed
+			// This means it missed our last task send, so we need to resend it
+			applog(LOG_WARNING, "%"PRIpreprv": No task request? Probably lost, resending task %d", proc->proc_repr, chip->last_taskid);
+			inc_hw_errors_only(thr);
+			timer_set_delay(&chip->tv_midtask_timeout, &tv_now, ROCKMINER_MIDTASK_RETRY_US);
+			if (!rockminer_send_work(thr))
+			{
+				rockminer_dead(dev);
+				timer_set_delay_from_now(&master_thr->tv_poll, ROCKMINER_RETRY_US);
+				inc_hw_errors_only(thr);
+				applogr(, LOG_ERR, "%"PRIpreprv": Failed to resend work", proc->proc_repr);
+			}
+		}
+	}
+	
+	timer_set_delay_from_now(&master_thr->tv_poll, ROCKMINER_POLL_US);
+}
+
+static
+const char *rockminer_set_clock(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct thr_info * const thr = proc->thr[0];
+	struct rockminer_chip_data * const chip = thr->cgpu_data;
+	
+	const int val = atoi(newvalue);
+	if (val < ROCKMINER_MIN_FREQ_MHZ || val > ROCKMINER_MAX_FREQ_MHZ)
+		return "Invalid clock speed";
+	
+	applog(LOG_DEBUG, "%"PRIpreprv": Changing clock frequency for future jobs to %d MHz", proc->proc_repr, val);
+	rockminer_job_buf_set_freq(chip->next_work_req, val);
+	
+	return NULL;
+}
+
+static const struct bfg_set_device_definition rockminer_set_device_funcs[] = {
+	{"clock", rockminer_set_clock, "clock frequency"},
+	{NULL}
+};
+
+static
+int rockminer_get_clock(struct cgpu_info * const proc)
+{
+	struct thr_info * const thr = proc->thr[0];
+	struct rockminer_chip_data * const chip = thr->cgpu_data;
+	return ((int)chip->next_work_req[0x31] + 1) * 10;
+}
+
+static
+struct api_data *rockminer_get_extra_device_status(struct cgpu_info * const proc)
+{
+	struct api_data *root = NULL;
+	
+	double d = rockminer_get_clock(proc);
+	root = api_add_freq(root, "Frequency", &d, true);
+	
+	return root;
+}
+
+#ifdef HAVE_CURSES
+static
+void rockminer_tui_wlogprint_choices(struct cgpu_info * const proc)
+{
+	wlogprint("[C]lock speed ");
+}
+
+static
+const char *rockminer_tui_handle_choice(struct cgpu_info * const proc, const int input)
+{
+	static char buf[0x100];  // Static for replies
+	
+	switch (input)
+	{
+		case 'c': case 'C':
+		{
+			sprintf(buf, "Set clock speed (range %d-%d, multiple of 10)", ROCKMINER_MIN_FREQ_MHZ, ROCKMINER_MAX_FREQ_MHZ);
+			char * const val = curses_input(buf);
+			const char * const msg = rockminer_set_clock(proc, "clock", val ?: "", NULL, NULL);
+			free(val);
+			if (msg)
+			{
+				snprintf(buf, sizeof(buf), "%s\n", msg);
+				return buf;
+			}
+			return "Clock speed changed\n";
+		}
+	}
+	return NULL;
+}
+
+static
+void rockminer_wlogprint_status(struct cgpu_info * const proc)
+{
+	wlogprint("Clock speed: %d\n", rockminer_get_clock(proc));
+}
+#endif
+
+struct device_drv rockminer_drv = {
+	.dname = "rockminer",
+	.name = "RKM",
+	
+	.lowl_match = rockminer_lowl_match,
+	.lowl_probe = rockminer_lowl_probe,
+	
+	.thread_init = rockminer_init,
+	
+	.minerloop = minerloop_queue,
+	.queue_append = rockminer_queue_append,
+	.queue_flush = rockminer_queue_flush,
+	.poll = rockminer_poll,
+	
+	.get_api_extra_device_status = rockminer_get_extra_device_status,
+	
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = rockminer_wlogprint_status,
+	.proc_tui_wlogprint_choices = rockminer_tui_wlogprint_choices,
+	.proc_tui_handle_choice = rockminer_tui_handle_choice,
+#endif
+};