Browse Source

USB automatically handle losing the device and report nodev in the API

Kano 13 years ago
parent
commit
34bcc1c66d
7 changed files with 165 additions and 20 deletions
  1. 1 0
      API-README
  2. 14 0
      api.c
  3. 29 1
      driver-bitforce.c
  4. 22 7
      driver-modminer.c
  5. 1 0
      miner.h
  6. 94 10
      usbutils.c
  7. 4 2
      usbutils.h

+ 1 - 0
API-README

@@ -419,6 +419,7 @@ Added API commands:
 
 Modified API commands:
  'pools' - add 'Best Share'
+ 'devs' and 'pga' - add 'No Device' for PGAs if MMQ or BFL compiled
 
 ----------
 

+ 14 - 0
api.c

@@ -391,6 +391,7 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_ZERINV 95
 #define MSG_ZERSUM 96
 #define MSG_ZERNOSUM 97
+#define MSG_USBNODEV 98
 
 enum code_severity {
 	SEVERITY_ERR,
@@ -568,6 +569,9 @@ struct CODES {
  { SEVERITY_ERR,   MSG_ZERINV,	PARAM_STR,	"Invalid zero parameter '%s'" },
  { SEVERITY_SUCC,  MSG_ZERSUM,	PARAM_STR,	"Zeroed %s stats with summary" },
  { SEVERITY_SUCC,  MSG_ZERNOSUM, PARAM_STR,	"Zeroed %s stats without summary" },
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+ { SEVERITY_ERR,   MSG_USBNODEV, PARAM_PGA,	"PGA%d has no device" },
+#endif
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
@@ -1570,6 +1574,9 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom
 		root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false);
 		root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false);
 		root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false);
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+		root = api_add_bool(root, "No Device", &(cgpu->nodev), false);
+#endif
 
 		root = print_data(root, buf, isjson, precom);
 		io_add(io_data, buf);
@@ -1785,6 +1792,13 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 	}
 #endif
 
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
+	if (cgpu->nodev) {
+		message(io_data, MSG_USBNODEV, id, NULL, isjson);
+		return;
+	}
+#endif
+
 	for (i = 0; i < mining_threads; i++) {
 		pga = thr_info[i].cgpu->cgminer_id;
 		if (pga == dev) {

+ 29 - 1
driver-bitforce.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Andrew Smith
+ * Copyright 2012-2013 Andrew Smith
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Con Kolivas
  *
@@ -82,6 +82,9 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: reset got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+	if (bitforce->nodev)
+		goto failed;
+
 	// Set data control
 	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_DATA,
 				FTDI_VALUE_DATA, bitforce->usbdev->found->interface, C_SETDATA);
@@ -89,6 +92,9 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: setdata got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+	if (bitforce->nodev)
+		goto failed;
+
 	// Set the baud
 	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD,
 				(FTDI_INDEX_BAUD & 0xff00) | bitforce->usbdev->found->interface,
@@ -97,6 +103,9 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: setbaud got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+	if (bitforce->nodev)
+		goto failed;
+
 	// Set Flow Control
 	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW,
 				FTDI_VALUE_FLOW, bitforce->usbdev->found->interface, C_SETFLOW);
@@ -104,6 +113,9 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: setflowctrl got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+	if (bitforce->nodev)
+		goto failed;
+
 	// Set Modem Control
 	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM,
 				FTDI_VALUE_MODEM, bitforce->usbdev->found->interface, C_SETMODEM);
@@ -111,6 +123,9 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+	if (bitforce->nodev)
+		goto failed;
+
 	// Clear any sent data
 	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
 				FTDI_VALUE_PURGE_TX, bitforce->usbdev->found->interface, C_PURGETX);
@@ -118,6 +133,9 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: purgetx got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+	if (bitforce->nodev)
+		goto failed;
+
 	// Clear any received data
 	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
 				FTDI_VALUE_PURGE_RX, bitforce->usbdev->found->interface, C_PURGERX);
@@ -125,6 +143,8 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 		applog(LOG_DEBUG, "%s%i: purgerx got err %d",
 			bitforce->drv->name, bitforce->device_id, err);
 
+failed:
+
 	if (lock)
 		mutex_unlock(&bitforce->device_mutex);
 }
@@ -322,6 +342,10 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	int err, amount;
 	char *s;
 
+	// Device is gone
+	if (bitforce->nodev)
+		return false;
+
 	/* Do not try to get the temperature if we're polling for a result to
 	 * minimise the chance of interleaved results */
 	if (bitforce->polling)
@@ -640,6 +664,10 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_
 	bool send_ret;
 	int64_t ret;
 
+	// Device is gone
+	if (bitforce->nodev)
+		return -1;
+
 	send_ret = bitforce_send_work(thr, work);
 
 	if (!restart_wait(bitforce->sleep_ms))

+ 22 - 7
driver-modminer.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Andrew Smith
+ * Copyright 2012-2013 Andrew Smith
  * Copyright 2012 Luke Dashjr
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -607,6 +607,7 @@ static bool modminer_fpga_prepare(struct thr_info *thr)
  *
  * N.B. clock must always be a multiple of 2
  */
+static const char *clocknodev = "clock failed - no device";
 static const char *clockoldwork = "clock already changed for this work";
 static const char *clocktoolow = "clock too low";
 static const char *clocktoohi = "clock too high";
@@ -620,6 +621,10 @@ static const char *modminer_delta_clock(struct thr_info *thr, int delta, bool te
 	unsigned char cmd[6], buf[1];
 	int err, amount;
 
+	// Device is gone
+	if (modminer->nodev)
+		return clocknodev;
+
 	// Only do once if multiple shares per work or multiple reasons
 	if (!state->new_work && !force)
 		return clockoldwork;
@@ -775,9 +780,6 @@ static bool modminer_start_work(struct thr_info *thr)
 	mutex_lock(modminer->modminer_mutex);
 
 	if ((err = usb_write(modminer, (char *)(state->next_work_cmd), 46, &amount, C_SENDWORK)) < 0 || amount != 46) {
-// TODO: err = LIBUSB_ERROR_NO_DEVICE means the MMQ disappeared
-// - need to delete it and rescan for it? (after a delay?)
-// but check all (4) disappeared
 		mutex_unlock(modminer->modminer_mutex);
 
 		applog(LOG_ERR, "%s%u: Start work failed (%d:%d)",
@@ -807,6 +809,10 @@ static void check_temperature(struct thr_info *thr)
 	int tbytes, tamount;
 	int amount;
 
+	// Device is gone
+	if (modminer->nodev)
+		return;
+
 	if (state->one_byte_temp) {
 		cmd[0] = MODMINER_TEMP1;
 		tbytes = 1;
@@ -891,6 +897,10 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 	double timeout;
 	int temploop;
 
+	// Device is gone
+	if (modminer->nodev)
+		return -1;
+
 	// If we are overheated it will just keep checking for results
 	// since we can't stop the work
 	// The next work will not start until the temp drops
@@ -904,9 +914,6 @@ static uint64_t modminer_process_results(struct thr_info *thr)
 	while (1) {
 		mutex_lock(modminer->modminer_mutex);
 		if ((err = usb_write(modminer, cmd, 2, &amount, C_REQUESTWORKSTATUS)) < 0 || amount != 2) {
-// TODO: err = LIBUSB_ERROR_NO_DEVICE means the MMQ disappeared
-// - need to delete it and rescan for it? (after a delay?)
-// but check all (4) disappeared
 			mutex_unlock(modminer->modminer_mutex);
 
 			// timeoutloop never resets so the timeouts can't
@@ -1053,6 +1060,10 @@ static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_
 	bool startwork;
 	struct timeval tv1, tv2;
 
+	// Device is gone
+	if (thr->cgpu->nodev)
+		return -1;
+
 	// Don't start new work if overheated
 	if (state->overheated == true) {
 		gettimeofday(&tv1, NULL);
@@ -1062,6 +1073,10 @@ static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_
 		while (state->overheated == true) {
 			check_temperature(thr);
 
+			// Device is gone
+			if (thr->cgpu->nodev)
+				return -1;
+
 			if (state->overheated == true) {
 				gettimeofday(&tv2, NULL);
 

+ 1 - 0
miner.h

@@ -399,6 +399,7 @@ struct cgpu_info {
 	};
 #if defined(USE_MODMINER) || defined(USE_BITFORCE)
 	int usbstat;
+	bool nodev;
 #endif
 #ifdef USE_MODMINER
 	char fpgaid;

+ 94 - 10
usbutils.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Andrew Smith
+ * Copyright 2012-2013 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
@@ -16,6 +16,10 @@
 #include "miner.h"
 #include "usbutils.h"
 
+#define NODEV(err) ((err) == LIBUSB_ERROR_NO_DEVICE || \
+			(err) == LIBUSB_ERROR_PIPE || \
+			(err) == LIBUSB_ERROR_OTHER)
+
 #ifdef USE_ICARUS
 #define DRV_ICARUS 1
 #endif
@@ -161,6 +165,7 @@ static int next_stat = 0;
 
 static const char **usb_commands;
 
+static const char *C_REJECTED_S = "RejectedNoDevice";
 static const char *C_PING_S = "Ping";
 static const char *C_CLEAR_S = "Clear";
 static const char *C_REQUESTVERSION_S = "RequestVersion";
@@ -551,6 +556,7 @@ static void cgusb_check_init()
 		// use constants so the stat generation is very quick
 		// and the association between number and name can't
 		// be missalined easily
+		usb_commands[C_REJECTED] = C_REJECTED_S;
 		usb_commands[C_PING] = C_PING_S;
 		usb_commands[C_CLEAR] = C_CLEAR_S;
 		usb_commands[C_REQUESTVERSION] = C_REQUESTVERSION_S;
@@ -673,8 +679,7 @@ static void release(uint8_t bus_number, uint8_t device_address, bool lock)
 	if (lock)
 		mutex_lock(list_lock);
 
-	usb_tmp = usb_head;
-	if (usb_tmp)
+	if ((usb_tmp = usb_head))
 		do {
 			if (bus_number == usb_tmp->bus_number
 			&&  device_address == usb_tmp->device_address) {
@@ -698,6 +703,8 @@ static void release(uint8_t bus_number, uint8_t device_address, bool lock)
 	if (usb_tmp->next == usb_tmp) {
 		usb_head = NULL;
 	} else {
+		if (usb_head == usb_tmp)
+			usb_head = usb_tmp->next;
 		usb_tmp->next->prev = usb_tmp->prev;
 		usb_tmp->prev->next = usb_tmp->next;
 	}
@@ -719,13 +726,6 @@ static void release_dev(libusb_device *dev, bool lock)
 	release(bus_number, device_address, lock);
 }
 
-#if 0
-static void release_cgusb(struct cg_usb_device *cgusb, bool lock)
-{
-	release(cgusb->bus_number, cgusb->device_address, lock);
-}
-#endif
-
 static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
 {
 	if (cgusb->serial_string && cgusb->serial_string != BLANK)
@@ -739,6 +739,8 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
 
 	free(cgusb->descriptor);
 
+	free(cgusb->found);
+
 	free(cgusb);
 
 	return NULL;
@@ -751,6 +753,31 @@ void usb_uninit(struct cgpu_info *cgpu)
 	cgpu->usbdev = free_cgusb(cgpu->usbdev);
 }
 
+void release_cgpu(struct cgpu_info *cgpu)
+{
+	struct cg_usb_device *cgusb = cgpu->usbdev;
+	uint8_t bus_number;
+	uint8_t device_address;
+	int i;
+
+	cgpu->nodev = true;
+
+	// Any devices sharing the same USB device should be marked also
+	// Currently only MMQ shares a USB device
+	for (i = 0; i < total_devices; i++)
+		if (devices[i] != cgpu && devices[i]->usbdev == cgusb) {
+			devices[i]->nodev = true;
+			devices[i]->usbdev = NULL;
+		}
+
+	bus_number = cgusb->bus_number;
+	device_address = cgusb->device_address;
+
+	usb_uninit(cgpu);
+
+	release(bus_number, device_address, true);
+}
+
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found)
 {
 	struct cg_usb_device *cgusb = NULL;
@@ -1172,6 +1199,19 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev
 	memcpy(&(details->item[item].last), tv_start, sizeof(tv_start));
 	details->item[item].count++;
 }
+
+static void rejected_inc(struct cgpu_info *cgpu)
+{
+	struct cg_usb_stats_details *details;
+	int item = CMD_ERROR;
+
+	if (cgpu->usbstat < 1)
+		newstats(cgpu);
+
+	details = &(usb_stats[cgpu->usbstat - 1].details[C_REJECTED * 2 + 0]);
+
+	details->item[item].count++;
+}
 #endif
 
 int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds cmd, bool ftdi)
@@ -1186,6 +1226,15 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 	int err, got, tot, i;
 	bool first = true;
 
+	if (cgpu->nodev) {
+		*buf = '\0';
+		*processed = 0;
+#if DO_USB_STATS
+		rejected_inc(cgpu);
+#endif
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
 	if (timeout == DEVTIMEOUT)
 		timeout = usbdev->found->timeout;
 
@@ -1212,6 +1261,11 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 
 		*processed = got;
 
+		if (NODEV(err)) {
+			cgpu->nodev = true;
+			release_cgpu(cgpu);
+		}
+
 		return err;
 	}
 
@@ -1268,6 +1322,11 @@ goteol:
 
 	*processed = tot;
 
+	if (NODEV(err)) {
+		cgpu->nodev = true;
+		release_cgpu(cgpu);
+	}
+
 	return err;
 }
 
@@ -1279,6 +1338,14 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 #endif
 	int err, sent;
 
+	if (cgpu->nodev) {
+		*processed = 0;
+#if DO_USB_STATS
+		rejected_inc(cgpu);
+#endif
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
 	sent = 0;
 	STATS_TIMEVAL(&tv_start);
 	err = libusb_bulk_transfer(usbdev->handle,
@@ -1291,6 +1358,11 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 
 	*processed = sent;
 
+	if (NODEV(err)) {
+		cgpu->nodev = true;
+		release_cgpu(cgpu);
+	}
+
 	return err;
 }
 
@@ -1302,6 +1374,13 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 #endif
 	int err;
 
+	if (cgpu->nodev) {
+#if DO_USB_STATS
+		rejected_inc(cgpu);
+#endif
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
 	STATS_TIMEVAL(&tv_start);
 	err = libusb_control_transfer(usbdev->handle, request_type,
 		bRequest, wValue, wIndex, NULL, 0,
@@ -1309,6 +1388,11 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 	STATS_TIMEVAL(&tv_finish);
 	USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0);
 
+	if (NODEV(err)) {
+		cgpu->nodev = true;
+		release_cgpu(cgpu);
+	}
+
 	return err;
 }
 

+ 4 - 2
usbutils.h

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Andrew Smith
+ * Copyright 2012-2013 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
@@ -79,7 +79,8 @@ struct cg_usb_device {
 };
 
 enum usb_cmds {
-	C_PING = 0,
+	C_REJECTED = 0,
+	C_PING,
 	C_CLEAR,
 	C_REQUESTVERSION,
 	C_GETVERSION,
@@ -120,6 +121,7 @@ struct device_drv;
 struct cgpu_info;
 
 void usb_uninit(struct cgpu_info *cgpu);
+void release_cgpu(struct cgpu_info *cgpu);
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
 void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));
 struct api_data *api_usb_stats(int *count);