|
@@ -0,0 +1,4472 @@
|
|
|
+/*
|
|
|
+ * Copyright 2012-2013 Andrew Smith
|
|
|
+ * Copyright 2013-2015 Con Kolivas <kernel@kolivas.org>
|
|
|
+ *
|
|
|
+ * 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 <ctype.h>
|
|
|
+#include <stdint.h>
|
|
|
+#include <stdbool.h>
|
|
|
+
|
|
|
+#include "logging.h"
|
|
|
+#include "miner.h"
|
|
|
+#include "usbutils.h"
|
|
|
+
|
|
|
+static pthread_mutex_t cgusb_lock;
|
|
|
+static pthread_mutex_t cgusbres_lock;
|
|
|
+static cglock_t cgusb_fd_lock;
|
|
|
+static cgtimer_t usb11_cgt;
|
|
|
+
|
|
|
+#define NODEV(err) ((err) != LIBUSB_SUCCESS && (err) != LIBUSB_ERROR_TIMEOUT)
|
|
|
+
|
|
|
+#define NOCONTROLDEV(err) ((err) < 0 && NODEV(err))
|
|
|
+
|
|
|
+/*
|
|
|
+ * WARNING - these assume DEVLOCK(cgpu, pstate) is called first and
|
|
|
+ * DEVUNLOCK(cgpu, pstate) in called in the same function with the same pstate
|
|
|
+ * given to DEVLOCK.
|
|
|
+ * You must call DEVUNLOCK(cgpu, pstate) before exiting the function or it will leave
|
|
|
+ * the thread Cancelability unrestored
|
|
|
+ */
|
|
|
+#define DEVWLOCK(cgpu, _pth_state) do { \
|
|
|
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_pth_state); \
|
|
|
+ cg_wlock(&cgpu->usbinfo.devlock); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define DEVWUNLOCK(cgpu, _pth_state) do { \
|
|
|
+ cg_wunlock(&cgpu->usbinfo.devlock); \
|
|
|
+ pthread_setcancelstate(_pth_state, NULL); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define DEVRLOCK(cgpu, _pth_state) do { \
|
|
|
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_pth_state); \
|
|
|
+ cg_rlock(&cgpu->usbinfo.devlock); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define DEVRUNLOCK(cgpu, _pth_state) do { \
|
|
|
+ cg_runlock(&cgpu->usbinfo.devlock); \
|
|
|
+ pthread_setcancelstate(_pth_state, NULL); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define USB_CONFIG 1
|
|
|
+
|
|
|
+#define BITFURY_TIMEOUT_MS 999
|
|
|
+#define DRILLBIT_TIMEOUT_MS 999
|
|
|
+#define ICARUS_TIMEOUT_MS 999
|
|
|
+
|
|
|
+// There is no windows version
|
|
|
+#define ANT_S1_TIMEOUT_MS 200
|
|
|
+#define ANT_S3_TIMEOUT_MS 200
|
|
|
+
|
|
|
+#ifdef WIN32
|
|
|
+#define BFLSC_TIMEOUT_MS 999
|
|
|
+#define BITFORCE_TIMEOUT_MS 999
|
|
|
+#define MODMINER_TIMEOUT_MS 999
|
|
|
+#define AVALON_TIMEOUT_MS 999
|
|
|
+#define AVALON4_TIMEOUT_MS 999
|
|
|
+#define AVALON7_TIMEOUT_MS 999
|
|
|
+#define AVALON8_TIMEOUT_MS 999
|
|
|
+#define AVALON9_TIMEOUT_MS 999
|
|
|
+#define AVALONLC3_TIMEOUT_MS 999
|
|
|
+#define AVALONM_TIMEOUT_MS 999
|
|
|
+#define KLONDIKE_TIMEOUT_MS 999
|
|
|
+#define COINTERRA_TIMEOUT_MS 999
|
|
|
+#define HASHFAST_TIMEOUT_MS 999
|
|
|
+#define HASHRATIO_TIMEOUT_MS 999
|
|
|
+#define BLOCKERUPTER_TIMEOUT_MS 999
|
|
|
+#define COMPAC_TIMEOUT_MS 999
|
|
|
+
|
|
|
+/* The safety timeout we use, cancelling async transfers on windows that fail
|
|
|
+ * to timeout on their own. */
|
|
|
+#define WIN_CALLBACK_EXTRA 40
|
|
|
+#define WIN_WRITE_CBEXTRA 5000
|
|
|
+#else
|
|
|
+#define BFLSC_TIMEOUT_MS 300
|
|
|
+#define BITFORCE_TIMEOUT_MS 200
|
|
|
+#define MODMINER_TIMEOUT_MS 100
|
|
|
+#define AVALON_TIMEOUT_MS 200
|
|
|
+#define AVALON4_TIMEOUT_MS 200
|
|
|
+#define AVALON7_TIMEOUT_MS 200
|
|
|
+#define AVALON8_TIMEOUT_MS 200
|
|
|
+#define AVALON9_TIMEOUT_MS 200
|
|
|
+#define AVALONLC3_TIMEOUT_MS 200
|
|
|
+#define AVALONM_TIMEOUT_MS 300
|
|
|
+#define KLONDIKE_TIMEOUT_MS 200
|
|
|
+#define COINTERRA_TIMEOUT_MS 200
|
|
|
+#define HASHFAST_TIMEOUT_MS 500
|
|
|
+#define HASHRATIO_TIMEOUT_MS 200
|
|
|
+#define BLOCKERUPTER_TIMEOUT_MS 300
|
|
|
+#define COMPAC_TIMEOUT_MS 300
|
|
|
+#endif
|
|
|
+
|
|
|
+#define USB_EPS(_intx, _epinfosx) { \
|
|
|
+ .interface = _intx, \
|
|
|
+ .ctrl_transfer = _intx, \
|
|
|
+ .epinfo_count = ARRAY_SIZE(_epinfosx), \
|
|
|
+ .epinfos = _epinfosx \
|
|
|
+ }
|
|
|
+
|
|
|
+#define USB_EPS_CTRL(_inty, _ctrlinty, _epinfosy) { \
|
|
|
+ .interface = _inty, \
|
|
|
+ .ctrl_transfer = _ctrlinty, \
|
|
|
+ .epinfo_count = ARRAY_SIZE(_epinfosy), \
|
|
|
+ .epinfos = _epinfosy \
|
|
|
+ }
|
|
|
+
|
|
|
+/* Linked list of all async transfers in progress. Protected by cgusb_fd_lock.
|
|
|
+ * This allows us to not stop the usb polling thread till all are complete, and
|
|
|
+ * to find cancellable transfers. */
|
|
|
+static struct list_head ut_list;
|
|
|
+
|
|
|
+#ifdef USE_BFLSC
|
|
|
+static struct usb_epinfo bflsc_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 512, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 512, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo bflsc_ints[] = {
|
|
|
+ USB_EPS(0, bflsc_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_BITFORCE
|
|
|
+// N.B. transfer size is 512 with USB2.0, but only 64 with USB1.1
|
|
|
+static struct usb_epinfo bfl_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo bfl_ints[] = {
|
|
|
+ USB_EPS(0, bfl_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_BITFURY
|
|
|
+static struct usb_epinfo bfu0_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo bfu1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 16, EPI(3), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 16, EPO(4), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+/* Default to interface 1 */
|
|
|
+static struct usb_intinfo bfu_ints[] = {
|
|
|
+ USB_EPS(1, bfu1_epinfos),
|
|
|
+ USB_EPS(0, bfu0_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo bxf0_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo bxf1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(2), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo bxf_ints[] = {
|
|
|
+ USB_EPS(1, bxf1_epinfos),
|
|
|
+ USB_EPS(0, bxf0_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo nfu_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 64, EPO(1), 0, 0 },
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo nfu_ints[] = {
|
|
|
+ USB_EPS(0, nfu_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo bxm_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 512, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 512, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo bxm_ints[] = {
|
|
|
+ USB_EPS(0, bxm_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_BLOCKERUPTER
|
|
|
+// BlockErupter Device
|
|
|
+static struct usb_epinfo bet_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo bet_ints[] = {
|
|
|
+ USB_EPS(0, bet_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_GEKKO
|
|
|
+// CP210X Devices
|
|
|
+static struct usb_epinfo gek1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo gek1_ints[] = {
|
|
|
+ USB_EPS(0, gek1_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+// FTDI Devices
|
|
|
+static struct usb_epinfo gek2_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+static struct usb_intinfo gek2_ints[] = {
|
|
|
+ USB_EPS(0, gek2_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_DRILLBIT
|
|
|
+// Drillbit Bitfury devices
|
|
|
+static struct usb_epinfo drillbit_int_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(3), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo drillbit_bulk_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 16, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 16, EPO(2), 0, 0 },
|
|
|
+};
|
|
|
+
|
|
|
+/* Default to interface 1 */
|
|
|
+static struct usb_intinfo drillbit_ints[] = {
|
|
|
+ USB_EPS(1, drillbit_bulk_epinfos),
|
|
|
+ USB_EPS(0, drillbit_int_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_HASHFAST
|
|
|
+#include "driver-hashfast.h"
|
|
|
+
|
|
|
+static struct usb_epinfo hfa0_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(3), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo hfa1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+/* Default to interface 1 */
|
|
|
+static struct usb_intinfo hfa_ints[] = {
|
|
|
+ USB_EPS(1, hfa1_epinfos),
|
|
|
+ USB_EPS(0, hfa0_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_HASHRATIO
|
|
|
+static struct usb_epinfo hro_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo hro_ints[] = {
|
|
|
+ USB_EPS(0, hro_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_MODMINER
|
|
|
+static struct usb_epinfo mmq_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(3), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo mmq_ints[] = {
|
|
|
+ USB_EPS(1, mmq_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_AVALON
|
|
|
+static struct usb_epinfo ava_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ava_ints[] = {
|
|
|
+ USB_EPS(0, ava_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_AVALON2
|
|
|
+static struct usb_epinfo ava2_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ava2_ints[] = {
|
|
|
+ USB_EPS(0, ava2_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_AVALON4
|
|
|
+static struct usb_epinfo ava4_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ava4_ints[] = {
|
|
|
+ USB_EPS(1, ava4_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON7
|
|
|
+static struct usb_epinfo ava7_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ava7_ints[] = {
|
|
|
+ USB_EPS(1, ava7_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON8
|
|
|
+static struct usb_epinfo ava8_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ava8_ints[] = {
|
|
|
+ USB_EPS(1, ava8_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON9
|
|
|
+static struct usb_epinfo ava9_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ava9_ints[] = {
|
|
|
+ USB_EPS(1, ava9_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALONLC3
|
|
|
+static struct usb_epinfo avalc3_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo avalc3_ints[] = {
|
|
|
+ USB_EPS(1, avalc3_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON_MINER
|
|
|
+static struct usb_epinfo avam_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 40, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 40, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo avam_ints[] = {
|
|
|
+ USB_EPS(1, avam_epinfos)
|
|
|
+};
|
|
|
+static struct usb_epinfo av3u_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 40, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 40, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo av3u_ints[] = {
|
|
|
+ USB_EPS(0, av3u_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+#ifdef USE_KLONDIKE
|
|
|
+static struct usb_epinfo kln_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo kln_ints[] = {
|
|
|
+ USB_EPS(0, kln_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo kli0_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo kli1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(2), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo kli_ints[] = {
|
|
|
+ USB_EPS(1, kli1_epinfos),
|
|
|
+ USB_EPS(0, kli0_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_ICARUS
|
|
|
+static struct usb_epinfo ica_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ica_ints[] = {
|
|
|
+ USB_EPS(0, ica_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo ica1_epinfos0[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_INTERRUPT, 16, EPI(0x82), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo ica1_epinfos1[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(0x81), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(0x01), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ica1_ints[] = {
|
|
|
+ USB_EPS(1, ica1_epinfos1),
|
|
|
+ USB_EPS(0, ica1_epinfos0)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo amu_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo amu_ints[] = {
|
|
|
+ USB_EPS(0, amu_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo llt_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo llt_ints[] = {
|
|
|
+ USB_EPS(0, llt_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo cmr1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo cmr1_ints[] = {
|
|
|
+ USB_EPS(0, cmr1_epinfos)
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_epinfo cmr2_epinfos0[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 }
|
|
|
+};
|
|
|
+static struct usb_epinfo cmr2_epinfos1[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(4), 0, 0 },
|
|
|
+};
|
|
|
+static struct usb_epinfo cmr2_epinfos2[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(5), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(6), 0, 0 },
|
|
|
+};
|
|
|
+static struct usb_epinfo cmr2_epinfos3[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(7), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(8), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo cmr2_ints[] = {
|
|
|
+ USB_EPS_CTRL(0, 1, cmr2_epinfos0),
|
|
|
+ USB_EPS_CTRL(1, 2, cmr2_epinfos1),
|
|
|
+ USB_EPS_CTRL(2, 3, cmr2_epinfos2),
|
|
|
+ USB_EPS_CTRL(3, 4, cmr2_epinfos3)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_COINTERRA
|
|
|
+static struct usb_epinfo cointerra_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo cointerra_ints[] = {
|
|
|
+ USB_EPS(0, cointerra_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_ANT_S1
|
|
|
+static struct usb_epinfo ants1_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ants1_ints[] = {
|
|
|
+ USB_EPS(0, ants1_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef USE_ANT_S3
|
|
|
+static struct usb_epinfo ants3_epinfos[] = {
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 },
|
|
|
+ { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 }
|
|
|
+};
|
|
|
+
|
|
|
+static struct usb_intinfo ants3_ints[] = {
|
|
|
+ USB_EPS(0, ants3_epinfos)
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#define IDVENDOR_FTDI 0x0403
|
|
|
+
|
|
|
+#define INTINFO(_ints) \
|
|
|
+ .intinfo_count = ARRAY_SIZE(_ints), \
|
|
|
+ .intinfos = _ints
|
|
|
+
|
|
|
+#define USBEP(_usbdev, _intinfo, _epinfo) (_usbdev->found->intinfos[_intinfo].epinfos[_epinfo].ep)
|
|
|
+#define THISIF(_found, _this) (_found->intinfos[_this].interface)
|
|
|
+#define USBIF(_usbdev, _this) THISIF(_usbdev->found, _this)
|
|
|
+
|
|
|
+static struct usb_find_devices find_dev[] = {
|
|
|
+#ifdef USE_BFLSC
|
|
|
+ /* Wish these guys would be more consistent with setting these fields */
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bflsc,
|
|
|
+ .name = "BAS",
|
|
|
+ .ident = IDENT_BAS,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iManufacturer = "FTDI",
|
|
|
+ .iProduct = "BitFORCE SHA256 SC",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BFLSC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(bflsc_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bflsc,
|
|
|
+ .name = "BAS",
|
|
|
+ .ident = IDENT_BAS,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iManufacturer = "Butterfly Labs",
|
|
|
+ .iProduct = "BitFORCE SHA256 SC",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BFLSC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(bflsc_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bflsc,
|
|
|
+ .name = "BMA",
|
|
|
+ .ident = IDENT_BMA,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iManufacturer = "BUTTERFLY LABS",
|
|
|
+ .iProduct = "BitFORCE SHA256 SC",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BFLSC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(bflsc_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bflsc,
|
|
|
+ .name = "BMA",
|
|
|
+ .ident = IDENT_BMA,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iManufacturer = "BUTTERFLY LABS",
|
|
|
+ .iProduct = "BitFORCE SC-28nm",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BFLSC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(bflsc_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bflsc,
|
|
|
+ .name = "BMA",
|
|
|
+ .ident = IDENT_BMA,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iManufacturer = "BUTTERFLY LABS",
|
|
|
+ .iProduct = "BitFORCE SHA256",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BFLSC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(bflsc_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_BITFORCE
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bitforce,
|
|
|
+ .name = "BFL",
|
|
|
+ .ident = IDENT_BFL,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iManufacturer = "Butterfly Labs Inc.",
|
|
|
+ .iProduct = "BitFORCE SHA256",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BITFORCE_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(bfl_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_BITFURY
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bitfury,
|
|
|
+ .name = "BF1",
|
|
|
+ .ident = IDENT_BF1,
|
|
|
+ .idVendor = 0x03eb,
|
|
|
+ .idProduct = 0x204b,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BITFURY_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ //.iManufacturer = "BPMC",
|
|
|
+ .iProduct = "Bitfury BF1",
|
|
|
+ INTINFO(bfu_ints)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bitfury,
|
|
|
+ .name = "BXF",
|
|
|
+ .ident = IDENT_BXF,
|
|
|
+ .idVendor = 0x198c,
|
|
|
+ .idProduct = 0xb1f1,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BITFURY_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ .iManufacturer = "c-scape",
|
|
|
+ .iProduct = "bi?fury",
|
|
|
+ INTINFO(bxf_ints)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bitfury,
|
|
|
+ .name = "OSM",
|
|
|
+ .ident = IDENT_OSM,
|
|
|
+ .idVendor = 0x198c,
|
|
|
+ .idProduct = 0xb1f1,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BITFURY_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ .iManufacturer = "c-scape",
|
|
|
+ .iProduct = "OneString",
|
|
|
+ INTINFO(bxf_ints)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bitfury,
|
|
|
+ .name = "NFU",
|
|
|
+ .ident = IDENT_NFU,
|
|
|
+ .idVendor = 0x04d8,
|
|
|
+ .idProduct = 0x00de,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BITFURY_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(nfu_ints)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_bitfury,
|
|
|
+ .name = "BXM",
|
|
|
+ .ident = IDENT_BXM,
|
|
|
+ .idVendor = 0x0403,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BITFURY_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(bxm_ints)
|
|
|
+ },
|
|
|
+#endif
|
|
|
+#ifdef USE_DRILLBIT
|
|
|
+ {
|
|
|
+ .drv = DRIVER_drillbit,
|
|
|
+ .name = "DRB",
|
|
|
+ .ident = IDENT_DRB,
|
|
|
+ .idVendor = 0x03eb,
|
|
|
+ .idProduct = 0x2404,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = DRILLBIT_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ .iManufacturer = "Drillbit Systems",
|
|
|
+ .iProduct = NULL, /* Can be Thumb or Eight, same driver */
|
|
|
+ INTINFO(drillbit_ints)
|
|
|
+ },
|
|
|
+#endif
|
|
|
+#ifdef USE_MODMINER
|
|
|
+ {
|
|
|
+ .drv = DRIVER_modminer,
|
|
|
+ .name = "MMQ",
|
|
|
+ .ident = IDENT_MMQ,
|
|
|
+ .idVendor = 0x1fc9,
|
|
|
+ .idProduct = 0x0003,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = MODMINER_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(mmq_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon,
|
|
|
+ .name = "BTB",
|
|
|
+ .ident = IDENT_BTB,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .iManufacturer = "Burnin Electronics",
|
|
|
+ .iProduct = "BitBurner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON_TIMEOUT_MS,
|
|
|
+ .latency = 10,
|
|
|
+ INTINFO(ava_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon,
|
|
|
+ .name = "BBF",
|
|
|
+ .ident = IDENT_BBF,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .iManufacturer = "Burnin Electronics",
|
|
|
+ .iProduct = "BitBurner Fury",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON_TIMEOUT_MS,
|
|
|
+ .latency = 10,
|
|
|
+ INTINFO(ava_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon,
|
|
|
+ .name = "AVA",
|
|
|
+ .ident = IDENT_AVA,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON_TIMEOUT_MS,
|
|
|
+ .latency = 10,
|
|
|
+ INTINFO(ava_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON2
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon2,
|
|
|
+ .name = "AV2",
|
|
|
+ .ident = IDENT_AV2,
|
|
|
+ .idVendor = 0x067b,
|
|
|
+ .idProduct = 0x2303,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ava2_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON4
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon4,
|
|
|
+ .name = "AV4",
|
|
|
+ .ident = IDENT_AV4,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x33f2,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "USB2IIC Converter",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON4_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ava4_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON7
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon7,
|
|
|
+ .name = "AV7",
|
|
|
+ .ident = IDENT_AV7,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x33f2,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "USB2IIC Converter",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON7_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ava7_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON8
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon8,
|
|
|
+ .name = "AV8",
|
|
|
+ .ident = IDENT_AV8,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x33f2,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "USB2IIC Converter",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON8_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ava8_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON9
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalon9,
|
|
|
+ .name = "AV9",
|
|
|
+ .ident = IDENT_AV9,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x33f2,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "USB2IIC Converter",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALON9_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ava9_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALONLC3
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalonlc3,
|
|
|
+ .name = "AVLC3",
|
|
|
+ .ident = IDENT_AVLC3,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x33f2,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "USB2IIC Converter",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALONLC3_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(avalc3_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_AVALON_MINER
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalonm,
|
|
|
+ .name = "AV4M",
|
|
|
+ .ident = IDENT_AVM,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x40f1,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "Avalon4 mini",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALONM_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(avam_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_avalonm,
|
|
|
+ .name = "AV3U",
|
|
|
+ .ident = IDENT_AVM,
|
|
|
+ .idVendor = 0x29f1,
|
|
|
+ .idProduct = 0x33f3,
|
|
|
+ .iManufacturer = "CANAAN",
|
|
|
+ .iProduct = "Avalon nano",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = AVALONM_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(av3u_ints) },
|
|
|
+
|
|
|
+#endif
|
|
|
+#ifdef USE_HASHFAST
|
|
|
+ {
|
|
|
+ .drv = DRIVER_hashfast,
|
|
|
+ .name = "HFA",
|
|
|
+ .ident = IDENT_HFA,
|
|
|
+ .idVendor = HF_USB_VENDOR_ID,
|
|
|
+ .idProduct = HF_USB_PRODUCT_ID_G1,
|
|
|
+ .iManufacturer = "HashFast LLC",
|
|
|
+ .iProduct = "M1 Module",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = HASHFAST_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(hfa_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_HASHRATIO
|
|
|
+ {
|
|
|
+ .drv = DRIVER_hashratio,
|
|
|
+ .name = "HRO",
|
|
|
+ .ident = IDENT_HRO,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = HASHRATIO_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(hro_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_KLONDIKE
|
|
|
+ {
|
|
|
+ .drv = DRIVER_klondike,
|
|
|
+ .name = "KLN",
|
|
|
+ .ident = IDENT_KLN,
|
|
|
+ .idVendor = 0x04D8,
|
|
|
+ .idProduct = 0xF60A,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = KLONDIKE_TIMEOUT_MS,
|
|
|
+ .latency = 10,
|
|
|
+ INTINFO(kln_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_klondike,
|
|
|
+ .name = "KLI",
|
|
|
+ .ident = IDENT_KLN,
|
|
|
+ .idVendor = 0x04D8,
|
|
|
+ .idProduct = 0xF60A,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = KLONDIKE_TIMEOUT_MS,
|
|
|
+ .latency = 10,
|
|
|
+ INTINFO(kli_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_ICARUS
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "ICA",
|
|
|
+ .ident = IDENT_ICA,
|
|
|
+ .idVendor = 0x067b,
|
|
|
+ .idProduct = 0x2303,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ica_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "ICA",
|
|
|
+ .ident = IDENT_AVA,
|
|
|
+ .idVendor = 0x1fc9,
|
|
|
+ .idProduct = 0x0083,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(ica1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "AMU",
|
|
|
+ .ident = IDENT_AMU,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(amu_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "LIN",
|
|
|
+ .ident = IDENT_LIN,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(amu_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "ANU",
|
|
|
+ .ident = IDENT_ANU,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(amu_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "BLT",
|
|
|
+ .ident = IDENT_BLT,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .iProduct = "FT232R USB UART",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(llt_ints) },
|
|
|
+ // For any that don't match the above "BLT"
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "LLT",
|
|
|
+ .ident = IDENT_LLT,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(llt_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "CMR",
|
|
|
+ .ident = IDENT_CMR1,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x6014,
|
|
|
+ .iProduct = "Cairnsmore1",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(cmr1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_icarus,
|
|
|
+ .name = "CMR",
|
|
|
+ .ident = IDENT_CMR2,
|
|
|
+ .idVendor = IDVENDOR_FTDI,
|
|
|
+ .idProduct = 0x8350,
|
|
|
+ .iProduct = "Cairnsmore1",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ICARUS_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(cmr2_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_COINTERRA
|
|
|
+ {
|
|
|
+ .drv = DRIVER_cointerra,
|
|
|
+ .name = "CTA",
|
|
|
+ .ident = IDENT_CTA,
|
|
|
+ .idVendor = 0x1cbe,
|
|
|
+ .idProduct = 0x0003,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COINTERRA_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_STD,
|
|
|
+ INTINFO(cointerra_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_ANT_S1
|
|
|
+ {
|
|
|
+ .drv = DRIVER_ants1,
|
|
|
+ .name = "ANT",
|
|
|
+ .ident = IDENT_ANT,
|
|
|
+ .idVendor = 0x4254,
|
|
|
+ .idProduct = 0x4153,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ANT_S1_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_ANTS1,
|
|
|
+ INTINFO(ants1_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_ANT_S3
|
|
|
+ {
|
|
|
+ .drv = DRIVER_ants3,
|
|
|
+ .name = "AS3",
|
|
|
+ .ident = IDENT_AS3,
|
|
|
+ .idVendor = 0x4254,
|
|
|
+ .idProduct = 0x4153,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = ANT_S3_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_ANTS3,
|
|
|
+ INTINFO(ants3_ints) },
|
|
|
+#endif
|
|
|
+#ifdef USE_BLOCKERUPTER
|
|
|
+ {
|
|
|
+ .drv = DRIVER_blockerupter,
|
|
|
+ .name = "BET",
|
|
|
+ .ident = IDENT_BET,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .config = 1,
|
|
|
+ .timeout = BLOCKERUPTER_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(bet_ints) },
|
|
|
+
|
|
|
+#endif
|
|
|
+#ifdef USE_GEKKO
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "BSC",
|
|
|
+ .ident = IDENT_BSC,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .iManufacturer = "bitshopperde",
|
|
|
+ .iProduct = "Compac BM1384 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "BSD",
|
|
|
+ .ident = IDENT_BSD,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .iManufacturer = "bitshopperde",
|
|
|
+ .iProduct = "2Pac BM1384 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "BSE",
|
|
|
+ .ident = IDENT_BSE,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .iManufacturer = "bitshopperde",
|
|
|
+ .iProduct = "Terminus BM1384 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSC",
|
|
|
+ .ident = IDENT_GSC,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .iManufacturer = "GekkoScience",
|
|
|
+ .iProduct = "Compac BM1384 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSD",
|
|
|
+ .ident = IDENT_GSD,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .iManufacturer = "GekkoScience",
|
|
|
+ .iProduct = "2Pac BM1384 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSE",
|
|
|
+ .ident = IDENT_GSE,
|
|
|
+ .idVendor = 0x10c4,
|
|
|
+ .idProduct = 0xea60,
|
|
|
+ .iManufacturer = "GekkoScience",
|
|
|
+ .iProduct = "Terminus BM1384 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek1_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSH",
|
|
|
+ .ident = IDENT_GSH,
|
|
|
+ .idVendor = 0x0403,
|
|
|
+ .idProduct = 0x6001,
|
|
|
+ .iManufacturer = "FTDI",
|
|
|
+ .iProduct = "FT232R USB UART",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek2_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSI",
|
|
|
+ .ident = IDENT_GSI,
|
|
|
+ .idVendor = 0x0403,
|
|
|
+ .idProduct = 0x6015,
|
|
|
+ .iManufacturer = "GekkoScience",
|
|
|
+ .iProduct = "R606 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek2_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSF",
|
|
|
+ .ident = IDENT_GSF,
|
|
|
+ .idVendor = 0x0403,
|
|
|
+ .idProduct = 0x6015,
|
|
|
+ .iManufacturer = "GekkoScience",
|
|
|
+ .iProduct = "CompacF Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek2_ints) },
|
|
|
+ {
|
|
|
+ .drv = DRIVER_gekko,
|
|
|
+ .name = "GSF",
|
|
|
+ .ident = IDENT_GSFM,
|
|
|
+ .idVendor = 0x0403,
|
|
|
+ .idProduct = 0x6015,
|
|
|
+ .iManufacturer = "GekkoScience",
|
|
|
+ .iProduct = "R909 Bitcoin Miner",
|
|
|
+ .config = 1,
|
|
|
+ .timeout = COMPAC_TIMEOUT_MS,
|
|
|
+ .latency = LATENCY_UNUSED,
|
|
|
+ INTINFO(gek2_ints) },
|
|
|
+#endif
|
|
|
+ { DRIVER_MAX, NULL, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, NULL }
|
|
|
+};
|
|
|
+
|
|
|
+#define STRBUFLEN 256
|
|
|
+static const char *BLANK = "";
|
|
|
+static const char *space = " ";
|
|
|
+static const char *nodatareturned = "no data returned ";
|
|
|
+
|
|
|
+#if 0 // enable USBDEBUG - only during development testing
|
|
|
+ static const char *debug_true_str = "true";
|
|
|
+ static const char *debug_false_str = "false";
|
|
|
+ static const char *nodevstr = "=NODEV";
|
|
|
+ #define bool_str(boo) ((boo) ? debug_true_str : debug_false_str)
|
|
|
+ #define isnodev(err) (NODEV(err) ? nodevstr : BLANK)
|
|
|
+ #define USBDEBUG(fmt, ...) applog(LOG_WARNING, fmt, ##__VA_ARGS__)
|
|
|
+#else
|
|
|
+ #define USBDEBUG(fmt, ...)
|
|
|
+#endif
|
|
|
+
|
|
|
+// For device limits by driver
|
|
|
+static struct driver_count {
|
|
|
+ int count;
|
|
|
+ int limit;
|
|
|
+} drv_count[DRIVER_MAX];
|
|
|
+
|
|
|
+// For device limits by list of bus/dev
|
|
|
+static struct usb_busdev {
|
|
|
+ int bus_number;
|
|
|
+ int device_address;
|
|
|
+#ifdef WIN32
|
|
|
+ void *resource1;
|
|
|
+ void *resource2;
|
|
|
+#else
|
|
|
+ int fd;
|
|
|
+#endif
|
|
|
+} *busdev;
|
|
|
+
|
|
|
+static int busdev_count = 0;
|
|
|
+
|
|
|
+// Total device limit
|
|
|
+static int total_count = 0;
|
|
|
+static int total_limit = 999999;
|
|
|
+
|
|
|
+struct usb_in_use_list {
|
|
|
+ struct usb_busdev in_use;
|
|
|
+ struct usb_in_use_list *prev;
|
|
|
+ struct usb_in_use_list *next;
|
|
|
+};
|
|
|
+
|
|
|
+// List of in use devices
|
|
|
+static struct usb_in_use_list *in_use_head = NULL;
|
|
|
+static struct usb_in_use_list *blacklist_head = NULL;
|
|
|
+
|
|
|
+struct resource_work {
|
|
|
+ bool lock;
|
|
|
+ const char *dname;
|
|
|
+ uint8_t bus_number;
|
|
|
+ uint8_t device_address;
|
|
|
+ struct resource_work *next;
|
|
|
+};
|
|
|
+
|
|
|
+// Pending work for the reslock thread
|
|
|
+struct resource_work *res_work_head = NULL;
|
|
|
+
|
|
|
+struct resource_reply {
|
|
|
+ uint8_t bus_number;
|
|
|
+ uint8_t device_address;
|
|
|
+ bool got;
|
|
|
+ struct resource_reply *next;
|
|
|
+};
|
|
|
+
|
|
|
+// Replies to lock requests
|
|
|
+struct resource_reply *res_reply_head = NULL;
|
|
|
+
|
|
|
+// Some stats need to always be defined
|
|
|
+#define SEQ0 0
|
|
|
+#define SEQ1 1
|
|
|
+
|
|
|
+// NONE must be 0 - calloced
|
|
|
+#define MODE_NONE 0
|
|
|
+#define MODE_CTRL_READ (1 << 0)
|
|
|
+#define MODE_CTRL_WRITE (1 << 1)
|
|
|
+#define MODE_BULK_READ (1 << 2)
|
|
|
+#define MODE_BULK_WRITE (1 << 3)
|
|
|
+
|
|
|
+// Set this to 0 to remove stats processing
|
|
|
+#define DO_USB_STATS 1
|
|
|
+
|
|
|
+static bool stats_initialised = false;
|
|
|
+
|
|
|
+#if DO_USB_STATS
|
|
|
+
|
|
|
+#define MODE_SEP_STR "+"
|
|
|
+#define MODE_NONE_STR "X"
|
|
|
+#define MODE_CTRL_READ_STR "cr"
|
|
|
+#define MODE_CTRL_WRITE_STR "cw"
|
|
|
+#define MODE_BULK_READ_STR "br"
|
|
|
+#define MODE_BULK_WRITE_STR "bw"
|
|
|
+
|
|
|
+// One for each CMD, TIMEOUT, ERROR
|
|
|
+struct cg_usb_stats_item {
|
|
|
+ uint64_t count;
|
|
|
+ double total_delay;
|
|
|
+ double min_delay;
|
|
|
+ double max_delay;
|
|
|
+ struct timeval first;
|
|
|
+ struct timeval last;
|
|
|
+};
|
|
|
+
|
|
|
+#define CMD_CMD 0
|
|
|
+#define CMD_TIMEOUT 1
|
|
|
+#define CMD_ERROR 2
|
|
|
+
|
|
|
+// One for each C_CMD
|
|
|
+struct cg_usb_stats_details {
|
|
|
+ int seq;
|
|
|
+ uint32_t modes;
|
|
|
+ struct cg_usb_stats_item item[CMD_ERROR+1];
|
|
|
+};
|
|
|
+
|
|
|
+// One for each device
|
|
|
+struct cg_usb_stats {
|
|
|
+ char *name;
|
|
|
+ int device_id;
|
|
|
+ struct cg_usb_stats_details *details;
|
|
|
+};
|
|
|
+
|
|
|
+static struct cg_usb_stats *usb_stats = NULL;
|
|
|
+static int next_stat = USB_NOSTAT;
|
|
|
+
|
|
|
+#define SECTOMS(s) ((int)((s) * 1000))
|
|
|
+
|
|
|
+#define USB_STATS(sgpu_, sta_, fin_, err_, mode_, cmd_, seq_, tmo_) \
|
|
|
+ stats(sgpu_, sta_, fin_, err_, mode_, cmd_, seq_, tmo_)
|
|
|
+#define STATS_TIMEVAL(tv_) cgtime(tv_)
|
|
|
+#define USB_REJECT(sgpu_, mode_) rejected_inc(sgpu_, mode_)
|
|
|
+
|
|
|
+#else
|
|
|
+#define USB_STATS(sgpu_, sta_, fin_, err_, mode_, cmd_, seq_, tmo_)
|
|
|
+#define STATS_TIMEVAL(tv_)
|
|
|
+#define USB_REJECT(sgpu_, mode_)
|
|
|
+
|
|
|
+#endif // DO_USB_STATS
|
|
|
+
|
|
|
+/* Create usb_commands array from USB_PARSE_COMMANDS macro in usbutils.h */
|
|
|
+char *usb_commands[] = {
|
|
|
+ USB_PARSE_COMMANDS(JUMPTABLE)
|
|
|
+ "Null"
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef EOL
|
|
|
+#undef EOL
|
|
|
+#endif
|
|
|
+#define EOL "\n"
|
|
|
+
|
|
|
+static const char *DESDEV = "Device";
|
|
|
+static const char *DESCON = "Config";
|
|
|
+static const char *DESSTR = "String";
|
|
|
+static const char *DESINT = "Interface";
|
|
|
+static const char *DESEP = "Endpoint";
|
|
|
+static const char *DESHID = "HID";
|
|
|
+static const char *DESRPT = "Report";
|
|
|
+static const char *DESPHY = "Physical";
|
|
|
+static const char *DESHUB = "Hub";
|
|
|
+
|
|
|
+static const char *EPIN = "In: ";
|
|
|
+static const char *EPOUT = "Out: ";
|
|
|
+static const char *EPX = "?: ";
|
|
|
+
|
|
|
+static const char *CONTROL = "Control";
|
|
|
+static const char *ISOCHRONOUS_X = "Isochronous+?";
|
|
|
+static const char *ISOCHRONOUS_N_X = "Isochronous+None+?";
|
|
|
+static const char *ISOCHRONOUS_N_D = "Isochronous+None+Data";
|
|
|
+static const char *ISOCHRONOUS_N_F = "Isochronous+None+Feedback";
|
|
|
+static const char *ISOCHRONOUS_N_I = "Isochronous+None+Implicit";
|
|
|
+static const char *ISOCHRONOUS_A_X = "Isochronous+Async+?";
|
|
|
+static const char *ISOCHRONOUS_A_D = "Isochronous+Async+Data";
|
|
|
+static const char *ISOCHRONOUS_A_F = "Isochronous+Async+Feedback";
|
|
|
+static const char *ISOCHRONOUS_A_I = "Isochronous+Async+Implicit";
|
|
|
+static const char *ISOCHRONOUS_D_X = "Isochronous+Adaptive+?";
|
|
|
+static const char *ISOCHRONOUS_D_D = "Isochronous+Adaptive+Data";
|
|
|
+static const char *ISOCHRONOUS_D_F = "Isochronous+Adaptive+Feedback";
|
|
|
+static const char *ISOCHRONOUS_D_I = "Isochronous+Adaptive+Implicit";
|
|
|
+static const char *ISOCHRONOUS_S_X = "Isochronous+Sync+?";
|
|
|
+static const char *ISOCHRONOUS_S_D = "Isochronous+Sync+Data";
|
|
|
+static const char *ISOCHRONOUS_S_F = "Isochronous+Sync+Feedback";
|
|
|
+static const char *ISOCHRONOUS_S_I = "Isochronous+Sync+Implicit";
|
|
|
+static const char *BULK = "Bulk";
|
|
|
+static const char *INTERRUPT = "Interrupt";
|
|
|
+static const char *UNKNOWN = "Unknown";
|
|
|
+
|
|
|
+static const char *destype(uint8_t bDescriptorType)
|
|
|
+{
|
|
|
+ switch (bDescriptorType) {
|
|
|
+ case LIBUSB_DT_DEVICE:
|
|
|
+ return DESDEV;
|
|
|
+ case LIBUSB_DT_CONFIG:
|
|
|
+ return DESCON;
|
|
|
+ case LIBUSB_DT_STRING:
|
|
|
+ return DESSTR;
|
|
|
+ case LIBUSB_DT_INTERFACE:
|
|
|
+ return DESINT;
|
|
|
+ case LIBUSB_DT_ENDPOINT:
|
|
|
+ return DESEP;
|
|
|
+ case LIBUSB_DT_HID:
|
|
|
+ return DESHID;
|
|
|
+ case LIBUSB_DT_REPORT:
|
|
|
+ return DESRPT;
|
|
|
+ case LIBUSB_DT_PHYSICAL:
|
|
|
+ return DESPHY;
|
|
|
+ case LIBUSB_DT_HUB:
|
|
|
+ return DESHUB;
|
|
|
+ }
|
|
|
+ return UNKNOWN;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *epdir(uint8_t bEndpointAddress)
|
|
|
+{
|
|
|
+ switch (bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) {
|
|
|
+ case LIBUSB_ENDPOINT_IN:
|
|
|
+ return EPIN;
|
|
|
+ case LIBUSB_ENDPOINT_OUT:
|
|
|
+ return EPOUT;
|
|
|
+ }
|
|
|
+ return EPX;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *epatt(uint8_t bmAttributes)
|
|
|
+{
|
|
|
+ switch(bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) {
|
|
|
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
|
|
|
+ return CONTROL;
|
|
|
+ case LIBUSB_TRANSFER_TYPE_BULK:
|
|
|
+ return BULK;
|
|
|
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
|
|
|
+ return INTERRUPT;
|
|
|
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
|
|
|
+ switch(bmAttributes & LIBUSB_ISO_SYNC_TYPE_MASK) {
|
|
|
+ case LIBUSB_ISO_SYNC_TYPE_NONE:
|
|
|
+ switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_DATA:
|
|
|
+ return ISOCHRONOUS_N_D;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
|
|
|
+ return ISOCHRONOUS_N_F;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
|
|
|
+ return ISOCHRONOUS_N_I;
|
|
|
+ }
|
|
|
+ return ISOCHRONOUS_N_X;
|
|
|
+ case LIBUSB_ISO_SYNC_TYPE_ASYNC:
|
|
|
+ switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_DATA:
|
|
|
+ return ISOCHRONOUS_A_D;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
|
|
|
+ return ISOCHRONOUS_A_F;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
|
|
|
+ return ISOCHRONOUS_A_I;
|
|
|
+ }
|
|
|
+ return ISOCHRONOUS_A_X;
|
|
|
+ case LIBUSB_ISO_SYNC_TYPE_ADAPTIVE:
|
|
|
+ switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_DATA:
|
|
|
+ return ISOCHRONOUS_D_D;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
|
|
|
+ return ISOCHRONOUS_D_F;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
|
|
|
+ return ISOCHRONOUS_D_I;
|
|
|
+ }
|
|
|
+ return ISOCHRONOUS_D_X;
|
|
|
+ case LIBUSB_ISO_SYNC_TYPE_SYNC:
|
|
|
+ switch(bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_DATA:
|
|
|
+ return ISOCHRONOUS_S_D;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
|
|
|
+ return ISOCHRONOUS_S_F;
|
|
|
+ case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
|
|
|
+ return ISOCHRONOUS_S_I;
|
|
|
+ }
|
|
|
+ return ISOCHRONOUS_S_X;
|
|
|
+ }
|
|
|
+ return ISOCHRONOUS_X;
|
|
|
+ }
|
|
|
+
|
|
|
+ return UNKNOWN;
|
|
|
+}
|
|
|
+
|
|
|
+static void append(char **buf, char *append, size_t *off, size_t *len)
|
|
|
+{
|
|
|
+ int new = strlen(append);
|
|
|
+ if ((new + *off) >= *len)
|
|
|
+ {
|
|
|
+ *len *= 2;
|
|
|
+ *buf = cgrealloc(*buf, *len);
|
|
|
+ }
|
|
|
+
|
|
|
+ strcpy(*buf + *off, append);
|
|
|
+ *off += new;
|
|
|
+}
|
|
|
+
|
|
|
+static bool setgetdes(ssize_t count, libusb_device *dev, struct libusb_device_handle *handle, struct libusb_config_descriptor **config, int cd, char **buf, size_t *off, size_t *len)
|
|
|
+{
|
|
|
+ char tmp[512];
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = libusb_set_configuration(handle, cd);
|
|
|
+ if (err) {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " ** dev %d: Failed to set config descriptor to %d, err %d",
|
|
|
+ (int)count, cd, err);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = libusb_get_active_config_descriptor(dev, config);
|
|
|
+ if (err) {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " ** dev %d: Failed to get active config descriptor set to %d, err %d",
|
|
|
+ (int)count, cd, err);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " ** dev %d: Set & Got active config descriptor to %d, err %d",
|
|
|
+ (int)count, cd, err);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static void usb_full(ssize_t *count, libusb_device *dev, char **buf, size_t *off, size_t *len, int level)
|
|
|
+{
|
|
|
+ struct libusb_device_descriptor desc;
|
|
|
+ uint8_t bus_number;
|
|
|
+ uint8_t device_address;
|
|
|
+ struct libusb_device_handle *handle;
|
|
|
+ struct libusb_config_descriptor *config;
|
|
|
+ const struct libusb_interface_descriptor *idesc;
|
|
|
+ const struct libusb_endpoint_descriptor *epdesc;
|
|
|
+ unsigned char man[STRBUFLEN+1];
|
|
|
+ unsigned char prod[STRBUFLEN+1];
|
|
|
+ unsigned char ser[STRBUFLEN+1];
|
|
|
+ char tmp[512];
|
|
|
+ int err, i, j, k;
|
|
|
+
|
|
|
+ err = libusb_get_device_descriptor(dev, &desc);
|
|
|
+ if (opt_usb_list_all && err) {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL ".USB dev %d: Failed to get descriptor, err %d",
|
|
|
+ (int)(++(*count)), err);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bus_number = libusb_get_bus_number(dev);
|
|
|
+ device_address = libusb_get_device_address(dev);
|
|
|
+
|
|
|
+ if (!opt_usb_list_all) {
|
|
|
+ bool known = false;
|
|
|
+
|
|
|
+ for (i = 0; find_dev[i].drv != DRIVER_MAX; i++)
|
|
|
+ if ((find_dev[i].idVendor == desc.idVendor) &&
|
|
|
+ (find_dev[i].idProduct == desc.idProduct)) {
|
|
|
+ known = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!known)
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ (*count)++;
|
|
|
+
|
|
|
+ if (level == 0) {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL ".USB dev %d: Bus %d Device %d ID: %04x:%04x",
|
|
|
+ (int)(*count), (int)bus_number, (int)device_address,
|
|
|
+ desc.idVendor, desc.idProduct);
|
|
|
+ } else {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL ".USB dev %d: Bus %d Device %d Device Descriptor:" EOL "\tLength: %d" EOL
|
|
|
+ "\tDescriptor Type: %s" EOL "\tUSB: %04x" EOL "\tDeviceClass: %d" EOL
|
|
|
+ "\tDeviceSubClass: %d" EOL "\tDeviceProtocol: %d" EOL "\tMaxPacketSize0: %d" EOL
|
|
|
+ "\tidVendor: %04x" EOL "\tidProduct: %04x" EOL "\tDeviceRelease: %x" EOL
|
|
|
+ "\tNumConfigurations: %d",
|
|
|
+ (int)(*count), (int)bus_number, (int)device_address,
|
|
|
+ (int)(desc.bLength), destype(desc.bDescriptorType),
|
|
|
+ desc.bcdUSB, (int)(desc.bDeviceClass), (int)(desc.bDeviceSubClass),
|
|
|
+ (int)(desc.bDeviceProtocol), (int)(desc.bMaxPacketSize0),
|
|
|
+ desc.idVendor, desc.idProduct, desc.bcdDevice,
|
|
|
+ (int)(desc.bNumConfigurations));
|
|
|
+ }
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+
|
|
|
+ err = libusb_open(dev, &handle);
|
|
|
+ if (err) {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " ** dev %d: Failed to open, err %d", (int)(*count), err);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, man, STRBUFLEN);
|
|
|
+ if (err < 0)
|
|
|
+ snprintf((char *)man, sizeof(man), "** err:(%d) %s", err, libusb_error_name(err));
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, STRBUFLEN);
|
|
|
+ if (err < 0)
|
|
|
+ snprintf((char *)prod, sizeof(prod), "** err:(%d) %s", err, libusb_error_name(err));
|
|
|
+
|
|
|
+ if (level == 0) {
|
|
|
+ libusb_close(handle);
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " Manufacturer: '%s'" EOL " Product: '%s'", man, prod);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (libusb_kernel_driver_active(handle, 0) == 1) {
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " * dev %d: kernel attached", (int)(*count));
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ }
|
|
|
+
|
|
|
+ err = libusb_get_active_config_descriptor(dev, &config);
|
|
|
+ if (err) {
|
|
|
+ if (!setgetdes(*count, dev, handle, &config, 1, buf, off, len)
|
|
|
+ && !setgetdes(*count, dev, handle, &config, 0, buf, off, len)) {
|
|
|
+ libusb_close(handle);
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " ** dev %d: Failed to set config descriptor to %d or %d",
|
|
|
+ (int)(*count), 1, 0);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " dev %d: Active Config:" EOL "\tDescriptorType: %s" EOL
|
|
|
+ "\tNumInterfaces: %d" EOL "\tConfigurationValue: %d" EOL
|
|
|
+ "\tAttributes: %d" EOL "\tMaxPower: %d",
|
|
|
+ (int)(*count), destype(config->bDescriptorType),
|
|
|
+ (int)(config->bNumInterfaces), (int)(config->iConfiguration),
|
|
|
+ (int)(config->bmAttributes), (int)(config->MaxPower));
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+
|
|
|
+ for (i = 0; i < (int)(config->bNumInterfaces); i++) {
|
|
|
+ for (j = 0; j < config->interface[i].num_altsetting; j++) {
|
|
|
+ idesc = &(config->interface[i].altsetting[j]);
|
|
|
+
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " _dev %d: Interface Descriptor %d:" EOL
|
|
|
+ "\tDescriptorType: %s" EOL "\tInterfaceNumber: %d" EOL
|
|
|
+ "\tNumEndpoints: %d" EOL "\tInterfaceClass: %d" EOL
|
|
|
+ "\tInterfaceSubClass: %d" EOL "\tInterfaceProtocol: %d",
|
|
|
+ (int)(*count), j, destype(idesc->bDescriptorType),
|
|
|
+ (int)(idesc->bInterfaceNumber),
|
|
|
+ (int)(idesc->bNumEndpoints),
|
|
|
+ (int)(idesc->bInterfaceClass),
|
|
|
+ (int)(idesc->bInterfaceSubClass),
|
|
|
+ (int)(idesc->bInterfaceProtocol));
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+
|
|
|
+ for (k = 0; k < (int)(idesc->bNumEndpoints); k++) {
|
|
|
+ epdesc = &(idesc->endpoint[k]);
|
|
|
+
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " __dev %d: Interface %d Endpoint %d:" EOL
|
|
|
+ "\tDescriptorType: %s" EOL
|
|
|
+ "\tEndpointAddress: %s0x%x" EOL
|
|
|
+ "\tAttributes: %s" EOL "\tMaxPacketSize: %d" EOL
|
|
|
+ "\tInterval: %d" EOL "\tRefresh: %d",
|
|
|
+ (int)(*count), (int)(idesc->bInterfaceNumber), k,
|
|
|
+ destype(epdesc->bDescriptorType),
|
|
|
+ epdir(epdesc->bEndpointAddress),
|
|
|
+ (int)(epdesc->bEndpointAddress),
|
|
|
+ epatt(epdesc->bmAttributes),
|
|
|
+ epdesc->wMaxPacketSize,
|
|
|
+ (int)(epdesc->bInterval),
|
|
|
+ (int)(epdesc->bRefresh));
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ libusb_free_config_descriptor(config);
|
|
|
+ config = NULL;
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, ser, STRBUFLEN);
|
|
|
+ if (err < 0)
|
|
|
+ snprintf((char *)ser, sizeof(ser), "** err:(%d) %s", err, libusb_error_name(err));
|
|
|
+
|
|
|
+ snprintf(tmp, sizeof(tmp), EOL " dev %d: More Info:" EOL "\tManufacturer: '%s'" EOL
|
|
|
+ "\tProduct: '%s'" EOL "\tSerial '%s'",
|
|
|
+ (int)(*count), man, prod, ser);
|
|
|
+ append(buf, tmp, off, len);
|
|
|
+
|
|
|
+ libusb_close(handle);
|
|
|
+}
|
|
|
+
|
|
|
+// Function to dump all USB devices
|
|
|
+void usb_all(int level)
|
|
|
+{
|
|
|
+ libusb_device **list;
|
|
|
+ ssize_t count, i, j;
|
|
|
+ char *buf;
|
|
|
+ size_t len, off;
|
|
|
+
|
|
|
+ count = libusb_get_device_list(NULL, &list);
|
|
|
+ if (count < 0) {
|
|
|
+ applog(LOG_ERR, "USB all: failed, err:(%d) %s", (int)count, libusb_error_name((int)count));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (count == 0)
|
|
|
+ applog(LOG_WARNING, "USB all: found no devices");
|
|
|
+ else
|
|
|
+ {
|
|
|
+ len = 10000;
|
|
|
+ buf = malloc(len+1);
|
|
|
+ if (unlikely(!buf))
|
|
|
+ quit(1, "USB failed to malloc buf in usb_all");
|
|
|
+
|
|
|
+ sprintf(buf, "USB all: found %d devices", (int)count);
|
|
|
+ off = strlen(buf);
|
|
|
+
|
|
|
+ if (!opt_usb_list_all)
|
|
|
+ append(&buf, " - listing known devices", &off, &len);
|
|
|
+
|
|
|
+ j = -1;
|
|
|
+ for (i = 0; i < count; i++)
|
|
|
+ usb_full(&j, list[i], &buf, &off, &len, level);
|
|
|
+
|
|
|
+ _applog(LOG_WARNING, buf, false);
|
|
|
+
|
|
|
+ free(buf);
|
|
|
+
|
|
|
+ if (j == -1)
|
|
|
+ applog(LOG_WARNING, "No known USB devices");
|
|
|
+ else
|
|
|
+ applog(LOG_WARNING, "%d %sUSB devices",
|
|
|
+ (int)(++j), opt_usb_list_all ? BLANK : "known ");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ libusb_free_device_list(list, 1);
|
|
|
+}
|
|
|
+
|
|
|
+static void cgusb_check_init()
|
|
|
+{
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (stats_initialised == false) {
|
|
|
+ // N.B. environment LIBUSB_DEBUG also sets libusb_set_debug()
|
|
|
+ if (opt_usbdump >= 0) {
|
|
|
+#if LIBUSB_API_VERSION >= 0x01000106
|
|
|
+ libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, opt_usbdump);
|
|
|
+#else
|
|
|
+ libusb_set_debug(NULL, opt_usbdump);
|
|
|
+#endif
|
|
|
+ usb_all(opt_usbdump);
|
|
|
+ }
|
|
|
+ stats_initialised = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+}
|
|
|
+
|
|
|
+const char *usb_cmdname(enum usb_cmds cmd)
|
|
|
+{
|
|
|
+ cgusb_check_init();
|
|
|
+
|
|
|
+ return usb_commands[cmd];
|
|
|
+}
|
|
|
+
|
|
|
+void usb_applog(struct cgpu_info *cgpu, enum usb_cmds cmd, char *msg, int amount, int err)
|
|
|
+{
|
|
|
+ if (msg && !*msg)
|
|
|
+ msg = NULL;
|
|
|
+
|
|
|
+ if (!msg && amount == 0 && err == LIBUSB_SUCCESS)
|
|
|
+ msg = (char *)nodatareturned;
|
|
|
+
|
|
|
+ applog(LOG_ERR, "%s%i: %s failed%s%s (err=%d amt=%d)",
|
|
|
+ cgpu->drv->name, cgpu->device_id,
|
|
|
+ usb_cmdname(cmd),
|
|
|
+ msg ? space : BLANK, msg ? msg : BLANK,
|
|
|
+ err, amount);
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef WIN32
|
|
|
+static void in_use_store_ress(uint8_t bus_number, uint8_t device_address, void *resource1, void *resource2)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp;
|
|
|
+ bool found = false, empty = true;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ in_use_tmp = in_use_head;
|
|
|
+ while (in_use_tmp) {
|
|
|
+ if (in_use_tmp->in_use.bus_number == (int)bus_number &&
|
|
|
+ in_use_tmp->in_use.device_address == (int)device_address) {
|
|
|
+ found = true;
|
|
|
+
|
|
|
+ if (in_use_tmp->in_use.resource1)
|
|
|
+ empty = false;
|
|
|
+ in_use_tmp->in_use.resource1 = resource1;
|
|
|
+
|
|
|
+ if (in_use_tmp->in_use.resource2)
|
|
|
+ empty = false;
|
|
|
+ in_use_tmp->in_use.resource2 = resource2;
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in_use_tmp = in_use_tmp->next;
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (found == false)
|
|
|
+ applog(LOG_ERR, "FAIL: USB store_ress not found (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ if (empty == false)
|
|
|
+ applog(LOG_ERR, "FAIL: USB store_ress not empty (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+}
|
|
|
+
|
|
|
+static void in_use_get_ress(uint8_t bus_number, uint8_t device_address, void **resource1, void **resource2)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp;
|
|
|
+ bool found = false, empty = false;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ in_use_tmp = in_use_head;
|
|
|
+ while (in_use_tmp) {
|
|
|
+ if (in_use_tmp->in_use.bus_number == (int)bus_number &&
|
|
|
+ in_use_tmp->in_use.device_address == (int)device_address) {
|
|
|
+ found = true;
|
|
|
+
|
|
|
+ if (!in_use_tmp->in_use.resource1)
|
|
|
+ empty = true;
|
|
|
+ *resource1 = in_use_tmp->in_use.resource1;
|
|
|
+ in_use_tmp->in_use.resource1 = NULL;
|
|
|
+
|
|
|
+ if (!in_use_tmp->in_use.resource2)
|
|
|
+ empty = true;
|
|
|
+ *resource2 = in_use_tmp->in_use.resource2;
|
|
|
+ in_use_tmp->in_use.resource2 = NULL;
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in_use_tmp = in_use_tmp->next;
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (found == false)
|
|
|
+ applog(LOG_ERR, "FAIL: USB get_lock not found (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ if (empty == true)
|
|
|
+ applog(LOG_ERR, "FAIL: USB get_lock empty (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+}
|
|
|
+#else
|
|
|
+
|
|
|
+static void in_use_store_fd(uint8_t bus_number, uint8_t device_address, int fd)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp;
|
|
|
+ bool found = false;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ in_use_tmp = in_use_head;
|
|
|
+ while (in_use_tmp) {
|
|
|
+ if (in_use_tmp->in_use.bus_number == (int)bus_number &&
|
|
|
+ in_use_tmp->in_use.device_address == (int)device_address) {
|
|
|
+ found = true;
|
|
|
+ in_use_tmp->in_use.fd = fd;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in_use_tmp = in_use_tmp->next;
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (found == false) {
|
|
|
+ applog(LOG_ERR, "FAIL: USB store_fd not found (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int in_use_get_fd(uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp;
|
|
|
+ bool found = false;
|
|
|
+ int fd = -1;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ in_use_tmp = in_use_head;
|
|
|
+ while (in_use_tmp) {
|
|
|
+ if (in_use_tmp->in_use.bus_number == (int)bus_number &&
|
|
|
+ in_use_tmp->in_use.device_address == (int)device_address) {
|
|
|
+ found = true;
|
|
|
+ fd = in_use_tmp->in_use.fd;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in_use_tmp = in_use_tmp->next;
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (found == false) {
|
|
|
+ applog(LOG_ERR, "FAIL: USB get_lock not found (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+ }
|
|
|
+ return fd;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static bool _in_use(struct usb_in_use_list *head, uint8_t bus_number,
|
|
|
+ uint8_t device_address)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp;
|
|
|
+ bool ret = false;
|
|
|
+
|
|
|
+ in_use_tmp = head;
|
|
|
+ while (in_use_tmp) {
|
|
|
+ if (in_use_tmp->in_use.bus_number == (int)bus_number &&
|
|
|
+ in_use_tmp->in_use.device_address == (int)device_address) {
|
|
|
+ ret = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in_use_tmp = in_use_tmp->next;
|
|
|
+ if (in_use_tmp == head)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static bool __is_in_use(uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ if (_in_use(in_use_head, bus_number, device_address))
|
|
|
+ return true;
|
|
|
+ if (_in_use(blacklist_head, bus_number, device_address))
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool is_in_use_bd(uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ bool ret;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ ret = __is_in_use(bus_number, device_address);
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static bool is_in_use(libusb_device *dev)
|
|
|
+{
|
|
|
+ return is_in_use_bd(libusb_get_bus_number(dev), libusb_get_device_address(dev));
|
|
|
+}
|
|
|
+
|
|
|
+static bool how_in_use(uint8_t bus_number, uint8_t device_address, bool *blacklisted)
|
|
|
+{
|
|
|
+ bool ret;
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ ret = _in_use(in_use_head, bus_number, device_address);
|
|
|
+ if (!ret) {
|
|
|
+ if (_in_use(blacklist_head, bus_number, device_address))
|
|
|
+ *blacklisted = true;
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void usb_list(void)
|
|
|
+{
|
|
|
+ struct libusb_device_descriptor desc;
|
|
|
+ struct libusb_device_handle *handle;
|
|
|
+ uint8_t bus_number;
|
|
|
+ uint8_t device_address;
|
|
|
+ libusb_device **list;
|
|
|
+ ssize_t count, i, j;
|
|
|
+ int err, total = 0;
|
|
|
+
|
|
|
+ count = libusb_get_device_list(NULL, &list);
|
|
|
+ if (count < 0) {
|
|
|
+ applog(LOG_ERR, "USB list: failed, err:(%d) %s", (int)count, libusb_error_name((int)count));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (count == 0) {
|
|
|
+ applog(LOG_WARNING, "USB list: found no devices");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ bool known = false, blacklisted = false, active;
|
|
|
+ unsigned char manuf[256], prod[256];
|
|
|
+ libusb_device *dev = list[i];
|
|
|
+
|
|
|
+ err = libusb_get_device_descriptor(dev, &desc);
|
|
|
+ if (err) {
|
|
|
+ applog(LOG_WARNING, "USB list: Failed to get descriptor %d", (int)i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ bus_number = libusb_get_bus_number(dev);
|
|
|
+ device_address = libusb_get_device_address(dev);
|
|
|
+
|
|
|
+ for (j = 0; find_dev[j].drv != DRIVER_MAX; j++) {
|
|
|
+ if ((find_dev[j].idVendor == desc.idVendor) &&
|
|
|
+ (find_dev[j].idProduct == desc.idProduct)) {
|
|
|
+ known = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!known)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ err = libusb_open(dev, &handle);
|
|
|
+ if (err) {
|
|
|
+ applog(LOG_WARNING, "USB list: Failed to open %d", (int)i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, manuf, 255);
|
|
|
+ libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, 255);
|
|
|
+ total++;
|
|
|
+ active = how_in_use(bus_number, device_address, &blacklisted);
|
|
|
+ simplelog(LOG_WARNING, "Bus %u Device %u ID: %04x:%04x %s %s %sactive %s",
|
|
|
+ bus_number, device_address, desc.idVendor, desc.idProduct,
|
|
|
+ manuf, prod, active ? "" : "in", blacklisted ? "blacklisted" : "");
|
|
|
+ }
|
|
|
+ libusb_free_device_list(list, 1);
|
|
|
+ simplelog(LOG_WARNING, "%d total known USB device%s", total, total > 1 ? "s": "");
|
|
|
+}
|
|
|
+
|
|
|
+static void add_in_use(uint8_t bus_number, uint8_t device_address, bool blacklist)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp, **head;
|
|
|
+ bool found = false;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ if (unlikely(!blacklist && __is_in_use(bus_number, device_address))) {
|
|
|
+ found = true;
|
|
|
+ goto nofway;
|
|
|
+ }
|
|
|
+ if (blacklist)
|
|
|
+ head = &blacklist_head;
|
|
|
+ else
|
|
|
+ head = &in_use_head;
|
|
|
+
|
|
|
+ in_use_tmp = cgcalloc(1, sizeof(*in_use_tmp));
|
|
|
+ in_use_tmp->in_use.bus_number = (int)bus_number;
|
|
|
+ in_use_tmp->in_use.device_address = (int)device_address;
|
|
|
+ in_use_tmp->next = in_use_head;
|
|
|
+ if (*head)
|
|
|
+ (*head)->prev = in_use_tmp;
|
|
|
+ *head = in_use_tmp;
|
|
|
+nofway:
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (found)
|
|
|
+ applog(LOG_ERR, "FAIL: USB add already in use (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+}
|
|
|
+
|
|
|
+static void __remove_in_use(uint8_t bus_number, uint8_t device_address, bool blacklist)
|
|
|
+{
|
|
|
+ struct usb_in_use_list *in_use_tmp, **head;
|
|
|
+ bool found = false;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+ if (blacklist)
|
|
|
+ head = &blacklist_head;
|
|
|
+ else
|
|
|
+ head = &in_use_head;
|
|
|
+
|
|
|
+ in_use_tmp = *head;
|
|
|
+ while (in_use_tmp) {
|
|
|
+ if (in_use_tmp->in_use.bus_number == (int)bus_number &&
|
|
|
+ in_use_tmp->in_use.device_address == (int)device_address) {
|
|
|
+ found = true;
|
|
|
+ if (in_use_tmp == *head) {
|
|
|
+ *head = (*head)->next;
|
|
|
+ if (*head)
|
|
|
+ (*head)->prev = NULL;
|
|
|
+ } else {
|
|
|
+ in_use_tmp->prev->next = in_use_tmp->next;
|
|
|
+ if (in_use_tmp->next)
|
|
|
+ in_use_tmp->next->prev = in_use_tmp->prev;
|
|
|
+ }
|
|
|
+ free(in_use_tmp);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ in_use_tmp = in_use_tmp->next;
|
|
|
+ if (in_use_tmp == *head)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+
|
|
|
+ if (!found) {
|
|
|
+ applog(LOG_ERR, "FAIL: USB remove not already in use (%d:%d)",
|
|
|
+ (int)bus_number, (int)device_address);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void remove_in_use(uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ __remove_in_use(bus_number, device_address, false);
|
|
|
+}
|
|
|
+
|
|
|
+static bool cgminer_usb_lock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ struct resource_work *res_work;
|
|
|
+ bool ret;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "USB lock %s %d-%d", drv->dname, (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ res_work = cgcalloc(1, sizeof(*res_work));
|
|
|
+ res_work->lock = true;
|
|
|
+ res_work->dname = (const char *)(drv->dname);
|
|
|
+ res_work->bus_number = bus_number;
|
|
|
+ res_work->device_address = device_address;
|
|
|
+
|
|
|
+ mutex_lock(&cgusbres_lock);
|
|
|
+ res_work->next = res_work_head;
|
|
|
+ res_work_head = res_work;
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+
|
|
|
+ cgsem_post(&usb_resource_sem);
|
|
|
+
|
|
|
+ // TODO: add a timeout fail - restart the resource thread?
|
|
|
+ while (true) {
|
|
|
+ cgsleep_ms(50);
|
|
|
+
|
|
|
+ mutex_lock(&cgusbres_lock);
|
|
|
+ if (res_reply_head) {
|
|
|
+ struct resource_reply *res_reply_prev = NULL;
|
|
|
+ struct resource_reply *res_reply = res_reply_head;
|
|
|
+ while (res_reply) {
|
|
|
+ if (res_reply->bus_number == bus_number &&
|
|
|
+ res_reply->device_address == device_address) {
|
|
|
+
|
|
|
+ if (res_reply_prev)
|
|
|
+ res_reply_prev->next = res_reply->next;
|
|
|
+ else
|
|
|
+ res_reply_head = res_reply->next;
|
|
|
+
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+
|
|
|
+ ret = res_reply->got;
|
|
|
+
|
|
|
+ free(res_reply);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ res_reply_prev = res_reply;
|
|
|
+ res_reply = res_reply->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static bool cgminer_usb_lock(struct device_drv *drv, libusb_device *dev)
|
|
|
+{
|
|
|
+ return cgminer_usb_lock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev));
|
|
|
+}
|
|
|
+
|
|
|
+static void cgminer_usb_unlock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ struct resource_work *res_work;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "USB unlock %s %d-%d", drv->dname, (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ res_work = cgcalloc(1, sizeof(*res_work));
|
|
|
+ res_work->lock = false;
|
|
|
+ res_work->dname = (const char *)(drv->dname);
|
|
|
+ res_work->bus_number = bus_number;
|
|
|
+ res_work->device_address = device_address;
|
|
|
+
|
|
|
+ mutex_lock(&cgusbres_lock);
|
|
|
+ res_work->next = res_work_head;
|
|
|
+ res_work_head = res_work;
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+
|
|
|
+ cgsem_post(&usb_resource_sem);
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+static void cgminer_usb_unlock(struct device_drv *drv, libusb_device *dev)
|
|
|
+{
|
|
|
+ cgminer_usb_unlock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev));
|
|
|
+}
|
|
|
+
|
|
|
+static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
|
|
|
+{
|
|
|
+ applog(LOG_DEBUG, "USB free %s", cgusb->found->name);
|
|
|
+
|
|
|
+ if (cgusb->serial_string && cgusb->serial_string != BLANK)
|
|
|
+ free(cgusb->serial_string);
|
|
|
+
|
|
|
+ if (cgusb->manuf_string && cgusb->manuf_string != BLANK)
|
|
|
+ free(cgusb->manuf_string);
|
|
|
+
|
|
|
+ if (cgusb->prod_string && cgusb->prod_string != BLANK)
|
|
|
+ free(cgusb->prod_string);
|
|
|
+
|
|
|
+ if (cgusb->descriptor)
|
|
|
+ free(cgusb->descriptor);
|
|
|
+
|
|
|
+ free(cgusb->found);
|
|
|
+
|
|
|
+ free(cgusb);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void _usb_uninit(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ int ifinfo;
|
|
|
+
|
|
|
+ // May have happened already during a failed initialisation
|
|
|
+ // if release_cgpu() was called due to a USB NODEV(err)
|
|
|
+ if (!cgpu->usbdev)
|
|
|
+ return;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "USB uninit %s%i",
|
|
|
+ cgpu->drv->name, cgpu->device_id);
|
|
|
+
|
|
|
+ if (cgpu->usbdev->handle) {
|
|
|
+ for (ifinfo = cgpu->usbdev->found->intinfo_count - 1; ifinfo >= 0; ifinfo--) {
|
|
|
+ libusb_release_interface(cgpu->usbdev->handle,
|
|
|
+ THISIF(cgpu->usbdev->found, ifinfo));
|
|
|
+#ifdef LINUX
|
|
|
+ libusb_attach_kernel_driver(cgpu->usbdev->handle, THISIF(cgpu->usbdev->found, ifinfo));
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ cg_wlock(&cgusb_fd_lock);
|
|
|
+ libusb_close(cgpu->usbdev->handle);
|
|
|
+ cgpu->usbdev->handle = NULL;
|
|
|
+ cg_wunlock(&cgusb_fd_lock);
|
|
|
+ }
|
|
|
+ cgpu->usbdev = free_cgusb(cgpu->usbdev);
|
|
|
+}
|
|
|
+
|
|
|
+void usb_uninit(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ _usb_uninit(cgpu);
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+}
|
|
|
+
|
|
|
+/* We have dropped the read devlock before entering this function but we pick
|
|
|
+ * up the write lock to prevent any attempts to work on dereferenced code once
|
|
|
+ * the nodev flag has been set. */
|
|
|
+static bool __release_cgpu(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ struct cg_usb_device *cgusb = cgpu->usbdev;
|
|
|
+ bool initted = cgpu->usbinfo.initialised;
|
|
|
+ struct cgpu_info *lookcgpu;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ // It has already been done
|
|
|
+ if (cgpu->usbinfo.nodev)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "USB release %s%i",
|
|
|
+ cgpu->drv->name, cgpu->device_id);
|
|
|
+
|
|
|
+ if (initted) {
|
|
|
+ zombie_devs++;
|
|
|
+ total_count--;
|
|
|
+ drv_count[cgpu->drv->drv_id].count--;
|
|
|
+ }
|
|
|
+
|
|
|
+ cgpu->usbinfo.nodev = true;
|
|
|
+ cgpu->usbinfo.nodev_count++;
|
|
|
+ cgtime(&cgpu->usbinfo.last_nodev);
|
|
|
+
|
|
|
+ // Any devices sharing the same USB device should be marked also
|
|
|
+ for (i = 0; i < total_devices; i++) {
|
|
|
+ lookcgpu = get_devices(i);
|
|
|
+ if (lookcgpu != cgpu && lookcgpu->usbdev == cgusb) {
|
|
|
+ if (initted) {
|
|
|
+ total_count--;
|
|
|
+ drv_count[lookcgpu->drv->drv_id].count--;
|
|
|
+ }
|
|
|
+
|
|
|
+ lookcgpu->usbinfo.nodev = true;
|
|
|
+ lookcgpu->usbinfo.nodev_count++;
|
|
|
+ cg_memcpy(&(lookcgpu->usbinfo.last_nodev),
|
|
|
+ &(cgpu->usbinfo.last_nodev), sizeof(struct timeval));
|
|
|
+ lookcgpu->usbdev = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _usb_uninit(cgpu);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static void release_cgpu(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ if (__release_cgpu(cgpu))
|
|
|
+ cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
|
|
|
+}
|
|
|
+
|
|
|
+void blacklist_cgpu(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ if (cgpu->blacklisted) {
|
|
|
+ applog(LOG_WARNING, "Device already blacklisted");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ cgpu->blacklisted = true;
|
|
|
+ add_in_use(cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address, true);
|
|
|
+ if (__release_cgpu(cgpu))
|
|
|
+ cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
|
|
|
+}
|
|
|
+
|
|
|
+void whitelist_cgpu(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ if (!cgpu->blacklisted) {
|
|
|
+ applog(LOG_WARNING, "Device not blacklisted");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ __remove_in_use(cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address, true);
|
|
|
+ cgpu->blacklisted = false;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Force a NODEV on a device so it goes back to hotplug
|
|
|
+ */
|
|
|
+void usb_nodev(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ release_cgpu(cgpu);
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Use the same usbdev thus locking is across all related devices
|
|
|
+ */
|
|
|
+struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig)
|
|
|
+{
|
|
|
+ struct cgpu_info *copy;
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(orig, pstate);
|
|
|
+
|
|
|
+ copy = cgcalloc(1, sizeof(*copy));
|
|
|
+
|
|
|
+ copy->name = orig->name;
|
|
|
+ copy->drv = copy_drv(orig->drv);
|
|
|
+ copy->deven = orig->deven;
|
|
|
+ copy->threads = orig->threads;
|
|
|
+
|
|
|
+ copy->usbdev = orig->usbdev;
|
|
|
+
|
|
|
+ cg_memcpy(&(copy->usbinfo), &(orig->usbinfo), sizeof(copy->usbinfo));
|
|
|
+
|
|
|
+ copy->usbinfo.nodev = (copy->usbdev == NULL);
|
|
|
+
|
|
|
+ DEVWUNLOCK(orig, pstate);
|
|
|
+
|
|
|
+ return copy;
|
|
|
+}
|
|
|
+
|
|
|
+struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads)
|
|
|
+{
|
|
|
+ struct cgpu_info *cgpu = cgcalloc(1, sizeof(*cgpu));
|
|
|
+
|
|
|
+ cgpu->drv = drv;
|
|
|
+ cgpu->deven = DEV_ENABLED;
|
|
|
+ cgpu->threads = threads;
|
|
|
+
|
|
|
+ cgpu->usbinfo.nodev = true;
|
|
|
+
|
|
|
+ cglock_init(&cgpu->usbinfo.devlock);
|
|
|
+
|
|
|
+ return cgpu;
|
|
|
+}
|
|
|
+
|
|
|
+struct cgpu_info *usb_free_cgpu(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ if (cgpu->drv->copy)
|
|
|
+ free(cgpu->drv);
|
|
|
+
|
|
|
+ free(cgpu->device_path);
|
|
|
+
|
|
|
+ free(cgpu);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+#define USB_INIT_FAIL 0
|
|
|
+#define USB_INIT_OK 1
|
|
|
+#define USB_INIT_IGNORE 2
|
|
|
+
|
|
|
+static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found)
|
|
|
+{
|
|
|
+ unsigned char man[STRBUFLEN+1], prod[STRBUFLEN+1];
|
|
|
+ struct cg_usb_device *cgusb = NULL;
|
|
|
+ struct libusb_config_descriptor *config = NULL;
|
|
|
+ const struct libusb_interface_descriptor *idesc;
|
|
|
+ const struct libusb_endpoint_descriptor *epdesc;
|
|
|
+ unsigned char strbuf[STRBUFLEN+1];
|
|
|
+ char devpath[32];
|
|
|
+ char devstr[STRBUFLEN+1];
|
|
|
+ int err, ifinfo, epinfo, alt, epnum, pstate;
|
|
|
+ int bad = USB_INIT_FAIL;
|
|
|
+ int cfg, claimed = 0, i;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ cgpu->usbinfo.bus_number = libusb_get_bus_number(dev);
|
|
|
+ cgpu->usbinfo.device_address = libusb_get_device_address(dev);
|
|
|
+
|
|
|
+ if (found->intinfo_count > 1) {
|
|
|
+ snprintf(devpath, sizeof(devpath), "%d:%d-i%d",
|
|
|
+ (int)(cgpu->usbinfo.bus_number),
|
|
|
+ (int)(cgpu->usbinfo.device_address),
|
|
|
+ THISIF(found, 0));
|
|
|
+ } else {
|
|
|
+ snprintf(devpath, sizeof(devpath), "%d:%d",
|
|
|
+ (int)(cgpu->usbinfo.bus_number),
|
|
|
+ (int)(cgpu->usbinfo.device_address));
|
|
|
+ }
|
|
|
+
|
|
|
+ cgpu->device_path = strdup(devpath);
|
|
|
+
|
|
|
+ snprintf(devstr, sizeof(devstr), "- %s device %s", found->name, devpath);
|
|
|
+
|
|
|
+ cgusb = cgcalloc(1, sizeof(*cgusb));
|
|
|
+ cgusb->found = found;
|
|
|
+
|
|
|
+ if (found->idVendor == IDVENDOR_FTDI)
|
|
|
+ cgusb->usb_type = USB_TYPE_FTDI;
|
|
|
+
|
|
|
+ cgusb->ident = found->ident;
|
|
|
+
|
|
|
+ cgusb->descriptor = cgcalloc(1, sizeof(*(cgusb->descriptor)));
|
|
|
+
|
|
|
+ err = libusb_get_device_descriptor(dev, cgusb->descriptor);
|
|
|
+ if (err) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init failed to get descriptor, err %d %s",
|
|
|
+ err, devstr);
|
|
|
+ goto dame;
|
|
|
+ }
|
|
|
+
|
|
|
+ cg_wlock(&cgusb_fd_lock);
|
|
|
+ err = libusb_open(dev, &(cgusb->handle));
|
|
|
+ cg_wunlock(&cgusb_fd_lock);
|
|
|
+ if (err) {
|
|
|
+ switch (err) {
|
|
|
+ case LIBUSB_ERROR_ACCESS:
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "USB init, open device failed, err %d, "
|
|
|
+ "you don't have privilege to access %s",
|
|
|
+ err, devstr);
|
|
|
+ applog(LOG_ERR, "See README file included for help");
|
|
|
+ break;
|
|
|
+#ifdef WIN32
|
|
|
+ // Windows specific message
|
|
|
+ case LIBUSB_ERROR_NOT_SUPPORTED:
|
|
|
+ applog(LOG_ERR, "USB init, open device failed, err %d, ", err);
|
|
|
+ applog(LOG_ERR, "You need to install a WinUSB driver for %s", devstr);
|
|
|
+ applog(LOG_ERR, "And associate %s with WinUSB using zadig", devstr);
|
|
|
+ applog(LOG_ERR, "See README.txt file included for help");
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, open failed, err %d %s",
|
|
|
+ err, devstr);
|
|
|
+ }
|
|
|
+ goto dame;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef LINUX
|
|
|
+ for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) {
|
|
|
+ if (libusb_kernel_driver_active(cgusb->handle, THISIF(found, ifinfo)) == 1) {
|
|
|
+ applog(LOG_DEBUG, "USB init, kernel attached ... %s", devstr);
|
|
|
+ err = libusb_detach_kernel_driver(cgusb->handle, THISIF(found, ifinfo));
|
|
|
+ if (err == 0) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, kernel detached ifinfo %d interface %d"
|
|
|
+ " successfully %s",
|
|
|
+ ifinfo, THISIF(found, ifinfo), devstr);
|
|
|
+ } else {
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "USB init, kernel detach ifinfo %d interface %d failed,"
|
|
|
+ " err %d in use? %s",
|
|
|
+ ifinfo, THISIF(found, ifinfo), err, devstr);
|
|
|
+ goto nokernel;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(cgusb->handle,
|
|
|
+ cgusb->descriptor->iManufacturer,
|
|
|
+ man, STRBUFLEN);
|
|
|
+ if (err < 0) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, failed to get iManufacturer, err %d %s",
|
|
|
+ err, devstr);
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ if (found->iManufacturer) {
|
|
|
+ if (strcmp((char *)man, found->iManufacturer)) {
|
|
|
+ applog(LOG_DEBUG, "USB init, iManufacturer mismatch %s",
|
|
|
+ devstr);
|
|
|
+ applog(LOG_DEBUG, "Found %s vs %s", man, found->iManufacturer);
|
|
|
+ bad = USB_INIT_IGNORE;
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) {
|
|
|
+ const char *iManufacturer = find_dev[i].iManufacturer;
|
|
|
+ /* If other drivers has an iManufacturer set that match,
|
|
|
+ * don't try to claim this device. */
|
|
|
+
|
|
|
+ if (!iManufacturer)
|
|
|
+ continue;
|
|
|
+ /* If the alternative driver also has an iProduct, only
|
|
|
+ * use that for comparison. */
|
|
|
+ if (find_dev[i].iProduct)
|
|
|
+ continue;
|
|
|
+ if (!strcmp((char *)man, iManufacturer)) {
|
|
|
+ applog(LOG_DEBUG, "USB init, alternative iManufacturer match %s",
|
|
|
+ devstr);
|
|
|
+ applog(LOG_DEBUG, "Found %s", iManufacturer);
|
|
|
+ bad = USB_INIT_IGNORE;
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(cgusb->handle,
|
|
|
+ cgusb->descriptor->iProduct,
|
|
|
+ prod, STRBUFLEN);
|
|
|
+ if (err < 0) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, failed to get iProduct, err %d %s",
|
|
|
+ err, devstr);
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ if (found->iProduct) {
|
|
|
+ if (strcasecmp((char *)prod, found->iProduct)) {
|
|
|
+ applog(LOG_DEBUG, "USB init, iProduct mismatch %s",
|
|
|
+ devstr);
|
|
|
+ applog(LOG_DEBUG, "Found %s vs %s", prod, found->iProduct);
|
|
|
+ bad = USB_INIT_IGNORE;
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) {
|
|
|
+ const char *iProduct = find_dev[i].iProduct;
|
|
|
+ /* Do same for iProduct as iManufacturer above */
|
|
|
+
|
|
|
+ if (!iProduct)
|
|
|
+ continue;
|
|
|
+ if (!strcasecmp((char *)prod, iProduct)) {
|
|
|
+ applog(LOG_DEBUG, "USB init, alternative iProduct match %s",
|
|
|
+ devstr);
|
|
|
+ applog(LOG_DEBUG, "Found %s", iProduct);
|
|
|
+ bad = USB_INIT_IGNORE;
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg = -1;
|
|
|
+ err = libusb_get_configuration(cgusb->handle, &cfg);
|
|
|
+ if (err)
|
|
|
+ cfg = -1;
|
|
|
+
|
|
|
+ // Try to set it if we can't read it or it's different
|
|
|
+ if (cfg != found->config) {
|
|
|
+ err = libusb_set_configuration(cgusb->handle, found->config);
|
|
|
+ if (err) {
|
|
|
+ switch(err) {
|
|
|
+ case LIBUSB_ERROR_BUSY:
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "USB init, set config %d in use %s",
|
|
|
+ found->config, devstr);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, failed to set config to %d, err %d %s",
|
|
|
+ found->config, err, devstr);
|
|
|
+ }
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ err = libusb_get_active_config_descriptor(dev, &config);
|
|
|
+ if (err) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, failed to get config descriptor, err %d %s",
|
|
|
+ err, devstr);
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+
|
|
|
+ int imax = -1;
|
|
|
+ for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++)
|
|
|
+ if (found->intinfos[ifinfo].interface > imax)
|
|
|
+ imax = found->intinfos[ifinfo].interface;
|
|
|
+
|
|
|
+ if ((int)(config->bNumInterfaces) <= imax) {
|
|
|
+ applog(LOG_DEBUG, "USB init bNumInterfaces %d <= interface max %d for %s",
|
|
|
+ (int)(config->bNumInterfaces), imax, devstr);
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++)
|
|
|
+ for (epinfo = 0; epinfo < found->intinfos[ifinfo].epinfo_count; epinfo++)
|
|
|
+ found->intinfos[ifinfo].epinfos[epinfo].found = false;
|
|
|
+
|
|
|
+ for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) {
|
|
|
+ int interface = found->intinfos[ifinfo].interface;
|
|
|
+ for (alt = 0; alt < config->interface[interface].num_altsetting; alt++) {
|
|
|
+ idesc = &(config->interface[interface].altsetting[alt]);
|
|
|
+ for (epnum = 0; epnum < (int)(idesc->bNumEndpoints); epnum++) {
|
|
|
+ struct usb_epinfo *epinfos = found->intinfos[ifinfo].epinfos;
|
|
|
+ epdesc = &(idesc->endpoint[epnum]);
|
|
|
+ for (epinfo = 0; epinfo < found->intinfos[ifinfo].epinfo_count; epinfo++) {
|
|
|
+ if (!epinfos[epinfo].found) {
|
|
|
+ if (epdesc->bmAttributes == epinfos[epinfo].att
|
|
|
+ && epdesc->wMaxPacketSize >= epinfos[epinfo].size
|
|
|
+ && epdesc->bEndpointAddress == epinfos[epinfo].ep) {
|
|
|
+ epinfos[epinfo].found = true;
|
|
|
+ epinfos[epinfo].wMaxPacketSize = epdesc->wMaxPacketSize;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++)
|
|
|
+ for (epinfo = 0; epinfo < found->intinfos[ifinfo].epinfo_count; epinfo++)
|
|
|
+ if (found->intinfos[ifinfo].epinfos[epinfo].found == false) {
|
|
|
+ applog(LOG_DEBUG, "USB init found (%d,%d) == false %s",
|
|
|
+ ifinfo, epinfo, devstr);
|
|
|
+ goto cldame;
|
|
|
+ }
|
|
|
+
|
|
|
+ claimed = 0;
|
|
|
+ for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) {
|
|
|
+ err = libusb_claim_interface(cgusb->handle, THISIF(found, ifinfo));
|
|
|
+ if (err == 0)
|
|
|
+ claimed++;
|
|
|
+ else {
|
|
|
+ switch(err) {
|
|
|
+ case LIBUSB_ERROR_BUSY:
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "USB init, claim ifinfo %d interface %d in use %s",
|
|
|
+ ifinfo, THISIF(found, ifinfo), devstr);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init, claim ifinfo %d interface %d failed,"
|
|
|
+ " err %d %s",
|
|
|
+ ifinfo, THISIF(found, ifinfo), err, devstr);
|
|
|
+ }
|
|
|
+ goto reldame;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg = -1;
|
|
|
+ err = libusb_get_configuration(cgusb->handle, &cfg);
|
|
|
+ if (err)
|
|
|
+ cfg = -1;
|
|
|
+ if (cfg != found->config) {
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "USB init, incorrect config (%d!=%d) after claim of %s",
|
|
|
+ cfg, found->config, devstr);
|
|
|
+ goto reldame;
|
|
|
+ }
|
|
|
+
|
|
|
+ cgusb->usbver = cgusb->descriptor->bcdUSB;
|
|
|
+ if (cgusb->usbver < 0x0200) {
|
|
|
+ cgusb->usb11 = true;
|
|
|
+ cgusb->tt = true;
|
|
|
+ }
|
|
|
+
|
|
|
+// TODO: allow this with the right version of the libusb include and running library
|
|
|
+// cgusb->speed = libusb_get_device_speed(dev);
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(cgusb->handle,
|
|
|
+ cgusb->descriptor->iProduct, strbuf, STRBUFLEN);
|
|
|
+ if (err > 0)
|
|
|
+ cgusb->prod_string = strdup((char *)strbuf);
|
|
|
+ else
|
|
|
+ cgusb->prod_string = (char *)BLANK;
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(cgusb->handle,
|
|
|
+ cgusb->descriptor->iManufacturer, strbuf, STRBUFLEN);
|
|
|
+ if (err > 0)
|
|
|
+ cgusb->manuf_string = strdup((char *)strbuf);
|
|
|
+ else
|
|
|
+ cgusb->manuf_string = (char *)BLANK;
|
|
|
+
|
|
|
+ err = libusb_get_string_descriptor_ascii(cgusb->handle,
|
|
|
+ cgusb->descriptor->iSerialNumber, strbuf, STRBUFLEN);
|
|
|
+ if (err > 0)
|
|
|
+ cgusb->serial_string = strdup((char *)strbuf);
|
|
|
+ else
|
|
|
+ cgusb->serial_string = (char *)BLANK;
|
|
|
+
|
|
|
+// TODO: ?
|
|
|
+// cgusb->fwVersion <- for temp1/temp2 decision? or serial? (driver-modminer.c)
|
|
|
+// cgusb->interfaceVersion
|
|
|
+
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB init %s usbver=%04x prod='%s' manuf='%s' serial='%s'",
|
|
|
+ devstr, cgusb->usbver, cgusb->prod_string,
|
|
|
+ cgusb->manuf_string, cgusb->serial_string);
|
|
|
+
|
|
|
+ cgpu->usbdev = cgusb;
|
|
|
+ cgpu->usbinfo.nodev = false;
|
|
|
+
|
|
|
+ libusb_free_config_descriptor(config);
|
|
|
+
|
|
|
+ // Allow a name change based on the idVendor+idProduct
|
|
|
+ // N.B. must be done before calling add_cgpu()
|
|
|
+ if (strcasecmp(cgpu->drv->name, found->name)) {
|
|
|
+ if (!cgpu->drv->copy)
|
|
|
+ cgpu->drv = copy_drv(cgpu->drv);
|
|
|
+ cgpu->drv->name = (char *)(found->name);
|
|
|
+ }
|
|
|
+
|
|
|
+ bad = USB_INIT_OK;
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+reldame:
|
|
|
+
|
|
|
+ ifinfo = claimed;
|
|
|
+ while (ifinfo-- > 0)
|
|
|
+ libusb_release_interface(cgusb->handle, THISIF(found, ifinfo));
|
|
|
+
|
|
|
+cldame:
|
|
|
+#ifdef LINUX
|
|
|
+ libusb_attach_kernel_driver(cgusb->handle, THISIF(found, ifinfo));
|
|
|
+
|
|
|
+nokernel:
|
|
|
+#endif
|
|
|
+ cg_wlock(&cgusb_fd_lock);
|
|
|
+ libusb_close(cgusb->handle);
|
|
|
+ cgusb->handle = NULL;
|
|
|
+ cg_wunlock(&cgusb_fd_lock);
|
|
|
+
|
|
|
+dame:
|
|
|
+
|
|
|
+ if (config)
|
|
|
+ libusb_free_config_descriptor(config);
|
|
|
+
|
|
|
+ cgusb = free_cgusb(cgusb);
|
|
|
+
|
|
|
+out_unlock:
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return bad;
|
|
|
+}
|
|
|
+
|
|
|
+bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found_match)
|
|
|
+{
|
|
|
+ struct usb_find_devices *found_use = NULL;
|
|
|
+ int uninitialised_var(ret);
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) {
|
|
|
+ if (find_dev[i].drv == found_match->drv &&
|
|
|
+ find_dev[i].idVendor == found_match->idVendor &&
|
|
|
+ find_dev[i].idProduct == found_match->idProduct) {
|
|
|
+ found_use = cgmalloc(sizeof(*found_use));
|
|
|
+ cg_memcpy(found_use, &(find_dev[i]), sizeof(*found_use));
|
|
|
+
|
|
|
+ ret = _usb_init(cgpu, dev, found_use);
|
|
|
+
|
|
|
+ if (ret != USB_INIT_IGNORE)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret == USB_INIT_FAIL) {
|
|
|
+ applog(LOG_ERR, "%s detect (%d:%d) failed to initialise (incorrect device?), resetting",
|
|
|
+ cgpu->drv->dname,
|
|
|
+ (int)(cgpu->usbinfo.bus_number),
|
|
|
+ (int)(cgpu->usbinfo.device_address));
|
|
|
+ if (cgpu->usbdev && cgpu->usbdev->handle)
|
|
|
+ libusb_reset_device(cgpu->usbdev->handle);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (ret == USB_INIT_OK);
|
|
|
+}
|
|
|
+
|
|
|
+static bool usb_check_device(struct device_drv *drv, struct libusb_device *dev, struct usb_find_devices *look)
|
|
|
+{
|
|
|
+ struct libusb_device_descriptor desc;
|
|
|
+ int bus_number, device_address;
|
|
|
+ int err, i;
|
|
|
+ bool ok;
|
|
|
+
|
|
|
+ err = libusb_get_device_descriptor(dev, &desc);
|
|
|
+ if (err) {
|
|
|
+ applog(LOG_DEBUG, "USB check device: Failed to get descriptor, err %d", err);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (desc.idVendor != look->idVendor || desc.idProduct != look->idProduct) {
|
|
|
+ applog(LOG_DEBUG, "%s looking for %s %04x:%04x but found %04x:%04x instead",
|
|
|
+ drv->name, look->name, look->idVendor, look->idProduct, desc.idVendor, desc.idProduct);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (busdev_count > 0) {
|
|
|
+ bus_number = (int)libusb_get_bus_number(dev);
|
|
|
+ device_address = (int)libusb_get_device_address(dev);
|
|
|
+ ok = false;
|
|
|
+ for (i = 0; i < busdev_count; i++) {
|
|
|
+ if (bus_number == busdev[i].bus_number) {
|
|
|
+ if (busdev[i].device_address == -1 ||
|
|
|
+ device_address == busdev[i].device_address) {
|
|
|
+ ok = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!ok) {
|
|
|
+ applog(LOG_DEBUG, "%s rejected %s %04x:%04x with bus:dev (%d:%d)",
|
|
|
+ drv->name, look->name, look->idVendor, look->idProduct,
|
|
|
+ bus_number, device_address);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "%s looking for and found %s %04x:%04x",
|
|
|
+ drv->name, look->name, look->idVendor, look->idProduct);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static struct usb_find_devices *usb_check_each(int drvnum, struct device_drv *drv, struct libusb_device *dev)
|
|
|
+{
|
|
|
+ struct usb_find_devices *found;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; find_dev[i].drv != DRIVER_MAX; i++)
|
|
|
+ if (find_dev[i].drv == drvnum) {
|
|
|
+ if (usb_check_device(drv, dev, &(find_dev[i]))) {
|
|
|
+ found = cgmalloc(sizeof(*found));
|
|
|
+ cg_memcpy(found, &(find_dev[i]), sizeof(*found));
|
|
|
+ return found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+#define DRIVER_USB_CHECK_EACH(X) if (drv->drv_id == DRIVER_##X) \
|
|
|
+ return usb_check_each(DRIVER_##X, drv, dev);
|
|
|
+
|
|
|
+static struct usb_find_devices *usb_check(__maybe_unused struct device_drv *drv, __maybe_unused struct libusb_device *dev)
|
|
|
+{
|
|
|
+ if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB scan devices3: %s limit %d reached",
|
|
|
+ drv->dname, drv_count[drv->drv_id].limit);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ DRIVER_PARSE_COMMANDS(DRIVER_USB_CHECK_EACH)
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void __usb_detect(struct device_drv *drv, struct cgpu_info *(*device_detect)(struct libusb_device *, struct usb_find_devices *),
|
|
|
+ bool single)
|
|
|
+{
|
|
|
+ libusb_device **list;
|
|
|
+ ssize_t count, i;
|
|
|
+ struct usb_find_devices *found;
|
|
|
+ struct cgpu_info *cgpu;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "USB scan devices: checking for %s devices", drv->name);
|
|
|
+
|
|
|
+ if (total_count >= total_limit) {
|
|
|
+ applog(LOG_DEBUG, "USB scan devices: total limit %d reached", total_limit);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB scan devices: %s limit %d reached",
|
|
|
+ drv->dname, drv_count[drv->drv_id].limit);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ count = libusb_get_device_list(NULL, &list);
|
|
|
+ if (count < 0) {
|
|
|
+ applog(LOG_DEBUG, "USB scan devices: failed, err %d", (int)count);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (count == 0)
|
|
|
+ applog(LOG_DEBUG, "USB scan devices: found no devices");
|
|
|
+ else
|
|
|
+ cgsleep_ms(166);
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ if (total_count >= total_limit) {
|
|
|
+ applog(LOG_DEBUG, "USB scan devices2: total limit %d reached", total_limit);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) {
|
|
|
+ applog(LOG_DEBUG,
|
|
|
+ "USB scan devices2: %s limit %d reached",
|
|
|
+ drv->dname, drv_count[drv->drv_id].limit);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ found = usb_check(drv, list[i]);
|
|
|
+ if (found != NULL) {
|
|
|
+ bool new_dev = false;
|
|
|
+
|
|
|
+ if (is_in_use(list[i]) || cgminer_usb_lock(drv, list[i]) == false)
|
|
|
+ free(found);
|
|
|
+ else {
|
|
|
+ cgpu = device_detect(list[i], found);
|
|
|
+ if (!cgpu)
|
|
|
+ cgminer_usb_unlock(drv, list[i]);
|
|
|
+ else {
|
|
|
+ new_dev = true;
|
|
|
+ cgpu->usbinfo.initialised = true;
|
|
|
+ total_count++;
|
|
|
+ drv_count[drv->drv_id].count++;
|
|
|
+ }
|
|
|
+ free(found);
|
|
|
+ }
|
|
|
+ if (single && new_dev)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ libusb_free_device_list(list, 1);
|
|
|
+}
|
|
|
+
|
|
|
+#if DO_USB_STATS
|
|
|
+static void modes_str(char *buf, uint32_t modes)
|
|
|
+{
|
|
|
+ bool first;
|
|
|
+
|
|
|
+ *buf = '\0';
|
|
|
+
|
|
|
+ if (modes == MODE_NONE)
|
|
|
+ strcpy(buf, MODE_NONE_STR);
|
|
|
+ else {
|
|
|
+ first = true;
|
|
|
+
|
|
|
+ if (modes & MODE_CTRL_READ) {
|
|
|
+ strcpy(buf, MODE_CTRL_READ_STR);
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (modes & MODE_CTRL_WRITE) {
|
|
|
+ if (!first)
|
|
|
+ strcat(buf, MODE_SEP_STR);
|
|
|
+ strcat(buf, MODE_CTRL_WRITE_STR);
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (modes & MODE_BULK_READ) {
|
|
|
+ if (!first)
|
|
|
+ strcat(buf, MODE_SEP_STR);
|
|
|
+ strcat(buf, MODE_BULK_READ_STR);
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (modes & MODE_BULK_WRITE) {
|
|
|
+ if (!first)
|
|
|
+ strcat(buf, MODE_SEP_STR);
|
|
|
+ strcat(buf, MODE_BULK_WRITE_STR);
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+// The stat data can be spurious due to not locking it before copying it -
|
|
|
+// however that would require the stat() function to also lock and release
|
|
|
+// a mutex every time a usb read or write is called which would slow
|
|
|
+// things down more
|
|
|
+struct api_data *api_usb_stats(__maybe_unused int *count)
|
|
|
+{
|
|
|
+#if DO_USB_STATS
|
|
|
+ struct cg_usb_stats_details *details;
|
|
|
+ struct cg_usb_stats *sta;
|
|
|
+ struct api_data *root = NULL;
|
|
|
+ int device;
|
|
|
+ int cmdseq;
|
|
|
+ char modes_s[32];
|
|
|
+
|
|
|
+ if (next_stat == USB_NOSTAT)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ while (*count < next_stat * C_MAX * 2) {
|
|
|
+ device = *count / (C_MAX * 2);
|
|
|
+ cmdseq = *count % (C_MAX * 2);
|
|
|
+
|
|
|
+ (*count)++;
|
|
|
+
|
|
|
+ sta = &(usb_stats[device]);
|
|
|
+ details = &(sta->details[cmdseq]);
|
|
|
+
|
|
|
+ // Only show stats that have results
|
|
|
+ if (details->item[CMD_CMD].count == 0 &&
|
|
|
+ details->item[CMD_TIMEOUT].count == 0 &&
|
|
|
+ details->item[CMD_ERROR].count == 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ root = api_add_string(root, "Name", sta->name, false);
|
|
|
+ root = api_add_int(root, "ID", &(sta->device_id), false);
|
|
|
+ root = api_add_const(root, "Stat", usb_commands[cmdseq/2], false);
|
|
|
+ root = api_add_int(root, "Seq", &(details->seq), true);
|
|
|
+ modes_str(modes_s, details->modes);
|
|
|
+ root = api_add_string(root, "Modes", modes_s, true);
|
|
|
+ root = api_add_uint64(root, "Count",
|
|
|
+ &(details->item[CMD_CMD].count), true);
|
|
|
+ root = api_add_double(root, "Total Delay",
|
|
|
+ &(details->item[CMD_CMD].total_delay), true);
|
|
|
+ root = api_add_double(root, "Min Delay",
|
|
|
+ &(details->item[CMD_CMD].min_delay), true);
|
|
|
+ root = api_add_double(root, "Max Delay",
|
|
|
+ &(details->item[CMD_CMD].max_delay), true);
|
|
|
+ root = api_add_uint64(root, "Timeout Count",
|
|
|
+ &(details->item[CMD_TIMEOUT].count), true);
|
|
|
+ root = api_add_double(root, "Timeout Total Delay",
|
|
|
+ &(details->item[CMD_TIMEOUT].total_delay), true);
|
|
|
+ root = api_add_double(root, "Timeout Min Delay",
|
|
|
+ &(details->item[CMD_TIMEOUT].min_delay), true);
|
|
|
+ root = api_add_double(root, "Timeout Max Delay",
|
|
|
+ &(details->item[CMD_TIMEOUT].max_delay), true);
|
|
|
+ root = api_add_uint64(root, "Error Count",
|
|
|
+ &(details->item[CMD_ERROR].count), true);
|
|
|
+ root = api_add_double(root, "Error Total Delay",
|
|
|
+ &(details->item[CMD_ERROR].total_delay), true);
|
|
|
+ root = api_add_double(root, "Error Min Delay",
|
|
|
+ &(details->item[CMD_ERROR].min_delay), true);
|
|
|
+ root = api_add_double(root, "Error Max Delay",
|
|
|
+ &(details->item[CMD_ERROR].max_delay), true);
|
|
|
+ root = api_add_timeval(root, "First Command",
|
|
|
+ &(details->item[CMD_CMD].first), true);
|
|
|
+ root = api_add_timeval(root, "Last Command",
|
|
|
+ &(details->item[CMD_CMD].last), true);
|
|
|
+ root = api_add_timeval(root, "First Timeout",
|
|
|
+ &(details->item[CMD_TIMEOUT].first), true);
|
|
|
+ root = api_add_timeval(root, "Last Timeout",
|
|
|
+ &(details->item[CMD_TIMEOUT].last), true);
|
|
|
+ root = api_add_timeval(root, "First Error",
|
|
|
+ &(details->item[CMD_ERROR].first), true);
|
|
|
+ root = api_add_timeval(root, "Last Error",
|
|
|
+ &(details->item[CMD_ERROR].last), true);
|
|
|
+
|
|
|
+ return root;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+#if DO_USB_STATS
|
|
|
+static void newstats(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ mutex_lock(&cgusb_lock);
|
|
|
+
|
|
|
+ cgpu->usbinfo.usbstat = next_stat + 1;
|
|
|
+
|
|
|
+ usb_stats = cgrealloc(usb_stats, sizeof(*usb_stats) * (next_stat+1));
|
|
|
+ usb_stats[next_stat].name = cgpu->drv->name;
|
|
|
+ usb_stats[next_stat].device_id = -1;
|
|
|
+ usb_stats[next_stat].details = cgcalloc(2, sizeof(struct cg_usb_stats_details) * (C_MAX + 1));
|
|
|
+
|
|
|
+ for (i = 1; i < C_MAX * 2; i += 2)
|
|
|
+ usb_stats[next_stat].details[i].seq = 1;
|
|
|
+
|
|
|
+ next_stat++;
|
|
|
+
|
|
|
+ mutex_unlock(&cgusb_lock);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+void update_usb_stats(__maybe_unused struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+#if DO_USB_STATS
|
|
|
+ if (cgpu->usbinfo.usbstat < 1)
|
|
|
+ newstats(cgpu);
|
|
|
+
|
|
|
+ // we don't know the device_id until after add_cgpu()
|
|
|
+ usb_stats[cgpu->usbinfo.usbstat - 1].device_id = cgpu->device_id;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+#if DO_USB_STATS
|
|
|
+static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timeval *tv_finish, int err, int mode, enum usb_cmds cmd, int seq, int timeout)
|
|
|
+{
|
|
|
+ struct cg_usb_stats_details *details;
|
|
|
+ double diff;
|
|
|
+ int item, extrams;
|
|
|
+
|
|
|
+ if (cgpu->usbinfo.usbstat < 1)
|
|
|
+ newstats(cgpu);
|
|
|
+
|
|
|
+ cgpu->usbinfo.tmo_count++;
|
|
|
+
|
|
|
+ // timeout checks are only done when stats are enabled
|
|
|
+ extrams = SECTOMS(tdiff(tv_finish, tv_start)) - timeout;
|
|
|
+ if (extrams >= USB_TMO_0) {
|
|
|
+ uint32_t totms = (uint32_t)(timeout + extrams);
|
|
|
+ int offset = 0;
|
|
|
+
|
|
|
+ if (extrams >= USB_TMO_2) {
|
|
|
+ applog(LOG_INFO, "%s%i: TIMEOUT %s took %dms but was %dms",
|
|
|
+ cgpu->drv->name, cgpu->device_id,
|
|
|
+ usb_cmdname(cmd), totms, timeout) ;
|
|
|
+ offset = 2;
|
|
|
+ } else if (extrams >= USB_TMO_1)
|
|
|
+ offset = 1;
|
|
|
+
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].count++;
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].total_over += extrams;
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].total_tmo += timeout;
|
|
|
+ if (cgpu->usbinfo.usb_tmo[offset].min_tmo == 0) {
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].min_tmo = totms;
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].max_tmo = totms;
|
|
|
+ } else {
|
|
|
+ if (cgpu->usbinfo.usb_tmo[offset].min_tmo > totms)
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].min_tmo = totms;
|
|
|
+ if (cgpu->usbinfo.usb_tmo[offset].max_tmo < totms)
|
|
|
+ cgpu->usbinfo.usb_tmo[offset].max_tmo = totms;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ details = &(usb_stats[cgpu->usbinfo.usbstat - 1].details[cmd * 2 + seq]);
|
|
|
+ details->modes |= mode;
|
|
|
+
|
|
|
+ diff = tdiff(tv_finish, tv_start);
|
|
|
+
|
|
|
+ switch (err) {
|
|
|
+ case LIBUSB_SUCCESS:
|
|
|
+ item = CMD_CMD;
|
|
|
+ break;
|
|
|
+ case LIBUSB_ERROR_TIMEOUT:
|
|
|
+ item = CMD_TIMEOUT;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ item = CMD_ERROR;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (details->item[item].count == 0) {
|
|
|
+ details->item[item].min_delay = diff;
|
|
|
+ cg_memcpy(&(details->item[item].first), tv_start, sizeof(*tv_start));
|
|
|
+ } else if (diff < details->item[item].min_delay)
|
|
|
+ details->item[item].min_delay = diff;
|
|
|
+
|
|
|
+ if (diff > details->item[item].max_delay)
|
|
|
+ details->item[item].max_delay = diff;
|
|
|
+
|
|
|
+ details->item[item].total_delay += diff;
|
|
|
+ cg_memcpy(&(details->item[item].last), tv_start, sizeof(*tv_start));
|
|
|
+ details->item[item].count++;
|
|
|
+}
|
|
|
+
|
|
|
+static void rejected_inc(struct cgpu_info *cgpu, uint32_t mode)
|
|
|
+{
|
|
|
+ struct cg_usb_stats_details *details;
|
|
|
+ int item = CMD_ERROR;
|
|
|
+
|
|
|
+ if (cgpu->usbinfo.usbstat < 1)
|
|
|
+ newstats(cgpu);
|
|
|
+
|
|
|
+ details = &(usb_stats[cgpu->usbinfo.usbstat - 1].details[C_REJECTED * 2 + 0]);
|
|
|
+ details->modes |= mode;
|
|
|
+ details->item[item].count++;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+#define USB_RETRY_MAX 5
|
|
|
+
|
|
|
+struct usb_transfer {
|
|
|
+ cgsem_t cgsem;
|
|
|
+ struct libusb_transfer *transfer;
|
|
|
+ bool cancellable;
|
|
|
+ struct list_head list;
|
|
|
+};
|
|
|
+
|
|
|
+bool async_usb_transfers(void)
|
|
|
+{
|
|
|
+ bool ret;
|
|
|
+
|
|
|
+ cg_rlock(&cgusb_fd_lock);
|
|
|
+ ret = !list_empty(&ut_list);
|
|
|
+ cg_runlock(&cgusb_fd_lock);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* Cancellable transfers should only be labelled as such if it is safe for them
|
|
|
+ * to effectively mimic timing out early. This flag is usually used to signify
|
|
|
+ * a read is waiting on a non-critical response that takes a long time and the
|
|
|
+ * driver wishes it be aborted if work restart message has been sent. */
|
|
|
+void cancel_usb_transfers(void)
|
|
|
+{
|
|
|
+ struct usb_transfer *ut;
|
|
|
+ int cancellations = 0;
|
|
|
+
|
|
|
+ cg_wlock(&cgusb_fd_lock);
|
|
|
+ list_for_each_entry(ut, &ut_list, list) {
|
|
|
+ if (ut->cancellable) {
|
|
|
+ ut->cancellable = false;
|
|
|
+ libusb_cancel_transfer(ut->transfer);
|
|
|
+ cancellations++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cg_wunlock(&cgusb_fd_lock);
|
|
|
+
|
|
|
+ if (cancellations)
|
|
|
+ applog(LOG_DEBUG, "Cancelled %d USB transfers", cancellations);
|
|
|
+}
|
|
|
+
|
|
|
+static void init_usb_transfer(struct usb_transfer *ut)
|
|
|
+{
|
|
|
+ cgsem_init(&ut->cgsem);
|
|
|
+ ut->transfer = libusb_alloc_transfer(0);
|
|
|
+ if (unlikely(!ut->transfer))
|
|
|
+ quit(1, "Failed to libusb_alloc_transfer");
|
|
|
+ ut->transfer->user_data = ut;
|
|
|
+ ut->cancellable = false;
|
|
|
+}
|
|
|
+
|
|
|
+static void complete_usb_transfer(struct usb_transfer *ut)
|
|
|
+{
|
|
|
+ cg_wlock(&cgusb_fd_lock);
|
|
|
+ list_del(&ut->list);
|
|
|
+ cg_wunlock(&cgusb_fd_lock);
|
|
|
+
|
|
|
+ cgsem_destroy(&ut->cgsem);
|
|
|
+ libusb_free_transfer(ut->transfer);
|
|
|
+}
|
|
|
+
|
|
|
+static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer)
|
|
|
+{
|
|
|
+ struct usb_transfer *ut = transfer->user_data;
|
|
|
+
|
|
|
+ ut->cancellable = false;
|
|
|
+ cgsem_post(&ut->cgsem);
|
|
|
+}
|
|
|
+
|
|
|
+static int usb_transfer_toerr(int ret)
|
|
|
+{
|
|
|
+ if (ret <= 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ switch (ret) {
|
|
|
+ default:
|
|
|
+ case LIBUSB_TRANSFER_COMPLETED:
|
|
|
+ ret = LIBUSB_SUCCESS;
|
|
|
+ break;
|
|
|
+ case LIBUSB_TRANSFER_ERROR:
|
|
|
+ ret = LIBUSB_ERROR_IO;
|
|
|
+ break;
|
|
|
+ case LIBUSB_TRANSFER_TIMED_OUT:
|
|
|
+ case LIBUSB_TRANSFER_CANCELLED:
|
|
|
+ ret = LIBUSB_ERROR_TIMEOUT;
|
|
|
+ break;
|
|
|
+ case LIBUSB_TRANSFER_STALL:
|
|
|
+ ret = LIBUSB_ERROR_PIPE;
|
|
|
+ break;
|
|
|
+ case LIBUSB_TRANSFER_NO_DEVICE:
|
|
|
+ ret = LIBUSB_ERROR_NO_DEVICE;
|
|
|
+ break;
|
|
|
+ case LIBUSB_TRANSFER_OVERFLOW:
|
|
|
+ ret = LIBUSB_ERROR_OVERFLOW;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* Wait for callback function to tell us it has finished the USB transfer, but
|
|
|
+ * use our own timer to cancel the request if we go beyond the timeout. */
|
|
|
+static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int timeout)
|
|
|
+{
|
|
|
+ struct libusb_transfer *transfer= ut->transfer;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = cgsem_mswait(&ut->cgsem, timeout);
|
|
|
+ if (ret == ETIMEDOUT) {
|
|
|
+ /* We are emulating a timeout ourself here */
|
|
|
+ libusb_cancel_transfer(transfer);
|
|
|
+
|
|
|
+ /* Now wait for the callback function to be invoked. */
|
|
|
+ cgsem_wait(&ut->cgsem);
|
|
|
+ }
|
|
|
+ ret = transfer->status;
|
|
|
+ ret = usb_transfer_toerr(ret);
|
|
|
+
|
|
|
+ /* No need to sort out mutexes here since they won't be reused */
|
|
|
+ *transferred = transfer->actual_length;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int usb_submit_transfer(struct usb_transfer *ut, struct libusb_transfer *transfer,
|
|
|
+ bool cancellable, bool tt)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&ut->list);
|
|
|
+
|
|
|
+ cg_wlock(&cgusb_fd_lock);
|
|
|
+ /* Imitate a transaction translator for writes to usb1.1 devices */
|
|
|
+ if (tt)
|
|
|
+ cgsleep_ms_r(&usb11_cgt, 1);
|
|
|
+ err = libusb_submit_transfer(transfer);
|
|
|
+ if (likely(!err))
|
|
|
+ ut->cancellable = cancellable;
|
|
|
+ list_add(&ut->list, &ut_list);
|
|
|
+ if (tt)
|
|
|
+ cgtimer_time(&usb11_cgt);
|
|
|
+ cg_wunlock(&cgusb_fd_lock);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+usb_perform_transfer(struct cgpu_info *cgpu, struct cg_usb_device *usbdev, int intinfo,
|
|
|
+ int epinfo, unsigned char *data, int length, int *transferred,
|
|
|
+ unsigned int timeout, __maybe_unused int mode, enum usb_cmds cmd,
|
|
|
+ __maybe_unused int seq, bool cancellable, bool tt)
|
|
|
+{
|
|
|
+ int bulk_timeout, callback_timeout = timeout, err_retries = 0;
|
|
|
+ struct libusb_device_handle *dev_handle = usbdev->handle;
|
|
|
+ struct usb_epinfo *usb_epinfo;
|
|
|
+ struct usb_transfer ut;
|
|
|
+ unsigned char endpoint;
|
|
|
+ bool interrupt;
|
|
|
+ int err, errn;
|
|
|
+#if DO_USB_STATS
|
|
|
+ struct timeval tv_start, tv_finish;
|
|
|
+#endif
|
|
|
+ unsigned char buf[512];
|
|
|
+#ifdef WIN32
|
|
|
+ /* On windows the callback_timeout is a safety mechanism only. */
|
|
|
+ bulk_timeout = timeout;
|
|
|
+ callback_timeout += WIN_CALLBACK_EXTRA;
|
|
|
+#else
|
|
|
+ /* We give the transfer no timeout since we manage timeouts ourself on
|
|
|
+ * non windows. */
|
|
|
+ bulk_timeout = 0;
|
|
|
+#endif
|
|
|
+
|
|
|
+ usb_epinfo = &(usbdev->found->intinfos[intinfo].epinfos[epinfo]);
|
|
|
+ interrupt = usb_epinfo->att == LIBUSB_TRANSFER_TYPE_INTERRUPT;
|
|
|
+ endpoint = usb_epinfo->ep;
|
|
|
+
|
|
|
+ if (unlikely(!data)) {
|
|
|
+ applog(LOG_ERR, "USB error: usb_perform_transfer sent NULL data (%s,intinfo=%d,epinfo=%d,length=%d,timeout=%u,mode=%d,cmd=%s,seq=%d) endpoint=%d",
|
|
|
+ cgpu->drv->name, intinfo, epinfo, length, timeout, mode, usb_cmdname(cmd), seq, (int)endpoint);
|
|
|
+ err = LIBUSB_ERROR_IO;
|
|
|
+ goto out_fail;
|
|
|
+ }
|
|
|
+ /* Avoid any async transfers during shutdown to allow the polling
|
|
|
+ * thread to be shut down after all existing transfers are complete */
|
|
|
+ if (opt_lowmem || cgpu->shutdown)
|
|
|
+ return libusb_bulk_transfer(dev_handle, endpoint, data, length, transferred, timeout);
|
|
|
+err_retry:
|
|
|
+ init_usb_transfer(&ut);
|
|
|
+
|
|
|
+ if ((endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
|
|
|
+ cg_memcpy(buf, data, length);
|
|
|
+#ifndef HAVE_LIBUSB
|
|
|
+ /* Older versions may not have this feature so only enable it
|
|
|
+ * when we know we're compiling with included static libusb. We
|
|
|
+ * only do this for bulk transfer, not interrupt. */
|
|
|
+ if (!cgpu->nozlp && !interrupt)
|
|
|
+ ut.transfer->flags |= LIBUSB_TRANSFER_ADD_ZERO_PACKET;
|
|
|
+#endif
|
|
|
+#ifdef WIN32
|
|
|
+ /* Writes on windows really don't like to be cancelled, but
|
|
|
+ * are prone to timeouts under heavy USB traffic, so make this
|
|
|
+ * a last resort cancellation delayed long after the write
|
|
|
+ * would have timed out on its own. */
|
|
|
+ callback_timeout += WIN_WRITE_CBEXTRA;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @usb_perform_transfer(%s (nodev=%s),intinfo=%d,epinfo=%d,data=%p,length=%d,timeout=%u,mode=%d,cmd=%s,seq=%d) endpoint=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, data, length, timeout, mode, usb_cmdname(cmd), seq, (int)endpoint);
|
|
|
+
|
|
|
+ if (interrupt) {
|
|
|
+ libusb_fill_interrupt_transfer(ut.transfer, dev_handle, endpoint,
|
|
|
+ buf, length, transfer_callback, &ut,
|
|
|
+ bulk_timeout);
|
|
|
+ } else {
|
|
|
+ libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf,
|
|
|
+ length, transfer_callback, &ut, bulk_timeout);
|
|
|
+ }
|
|
|
+ STATS_TIMEVAL(&tv_start);
|
|
|
+ err = usb_submit_transfer(&ut, ut.transfer, cancellable, tt);
|
|
|
+ errn = errno;
|
|
|
+ if (!err)
|
|
|
+ err = callback_wait(&ut, transferred, callback_timeout);
|
|
|
+ else
|
|
|
+ err = usb_transfer_toerr(err);
|
|
|
+ complete_usb_transfer(&ut);
|
|
|
+
|
|
|
+ STATS_TIMEVAL(&tv_finish);
|
|
|
+ USB_STATS(cgpu, &tv_start, &tv_finish, err, mode, cmd, seq, timeout);
|
|
|
+
|
|
|
+ if (err < 0) {
|
|
|
+ applog(LOG_DEBUG, "%s%i: %s (amt=%d err=%d ern=%d)",
|
|
|
+ cgpu->drv->name, cgpu->device_id,
|
|
|
+ usb_cmdname(cmd), *transferred, err, errn);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (err == LIBUSB_ERROR_PIPE) {
|
|
|
+ int pipeerr, retries = 0;
|
|
|
+
|
|
|
+ do {
|
|
|
+ cgpu->usbinfo.last_pipe = time(NULL);
|
|
|
+ cgpu->usbinfo.pipe_count++;
|
|
|
+ applog(LOG_INFO, "%s%i: libusb pipe error, trying to clear",
|
|
|
+ cgpu->drv->name, cgpu->device_id);
|
|
|
+ pipeerr = libusb_clear_halt(dev_handle, endpoint);
|
|
|
+ applog(LOG_DEBUG, "%s%i: libusb pipe error%scleared",
|
|
|
+ cgpu->drv->name, cgpu->device_id, err ? " not " : " ");
|
|
|
+
|
|
|
+ if (pipeerr)
|
|
|
+ cgpu->usbinfo.clear_fail_count++;
|
|
|
+ } while (pipeerr && ++retries < USB_RETRY_MAX);
|
|
|
+ if (!pipeerr && ++err_retries < USB_RETRY_MAX)
|
|
|
+ goto err_retry;
|
|
|
+ }
|
|
|
+ if (err == LIBUSB_ERROR_IO && ++err_retries < USB_RETRY_MAX)
|
|
|
+ goto err_retry;
|
|
|
+out_fail:
|
|
|
+ if (NODEV(err))
|
|
|
+ *transferred = 0;
|
|
|
+ else if ((endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN && *transferred)
|
|
|
+ cg_memcpy(data, buf, *transferred);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+void usb_reset(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ int pstate, err = 0;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+ if (!cgpu->usbinfo.nodev) {
|
|
|
+ err = libusb_reset_device(cgpu->usbdev->handle);
|
|
|
+ applog(LOG_WARNING, "%s %i attempted reset got err:(%d) %s",
|
|
|
+ cgpu->drv->name, cgpu->device_id, err, libusb_error_name(err));
|
|
|
+ }
|
|
|
+ if (NODEV(err))
|
|
|
+ release_cgpu(cgpu);
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+}
|
|
|
+
|
|
|
+int _usb_read(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t bufsiz,
|
|
|
+ int *processed, int timeout, const char *end, enum usb_cmds cmd, bool readonce, bool cancellable)
|
|
|
+{
|
|
|
+ unsigned char *ptr, usbbuf[USB_READ_BUFSIZE];
|
|
|
+ struct timeval read_start, tv_finish;
|
|
|
+ int bufleft, err, got, tot, pstate, tried_reset;
|
|
|
+ struct cg_usb_device *usbdev;
|
|
|
+ unsigned int initial_timeout;
|
|
|
+ bool first = true;
|
|
|
+ size_t usbbufread;
|
|
|
+ int endlen = 0;
|
|
|
+ char *eom = NULL;
|
|
|
+ double done;
|
|
|
+ bool ftdi;
|
|
|
+
|
|
|
+ memset(usbbuf, 0, USB_READ_BUFSIZE);
|
|
|
+ memset(buf, 0, bufsiz);
|
|
|
+
|
|
|
+ if (end)
|
|
|
+ endlen = strlen(end);
|
|
|
+
|
|
|
+ DEVRLOCK(cgpu, pstate);
|
|
|
+ if (cgpu->usbinfo.nodev) {
|
|
|
+ *processed = 0;
|
|
|
+ USB_REJECT(cgpu, MODE_BULK_READ);
|
|
|
+
|
|
|
+ err = LIBUSB_ERROR_NO_DEVICE;
|
|
|
+ goto out_noerrmsg;
|
|
|
+ }
|
|
|
+
|
|
|
+ usbdev = cgpu->usbdev;
|
|
|
+ /* Interrupt transfers are guaranteed to be of an expected size (we hope) */
|
|
|
+ if (usbdev->found->intinfos[intinfo].epinfos[epinfo].att == LIBUSB_TRANSFER_TYPE_INTERRUPT)
|
|
|
+ usbbufread = bufsiz;
|
|
|
+ else
|
|
|
+ usbbufread = 512;
|
|
|
+
|
|
|
+ ftdi = (usbdev->usb_type == USB_TYPE_FTDI);
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: _usb_read(%s (nodev=%s),intinfo=%d,epinfo=%d,buf=%p,bufsiz=%d,proc=%p,timeout=%u,end=%s,cmd=%s,ftdi=%s,readonce=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, buf, (int)bufsiz, processed, timeout, end ? (char *)str_text((char *)end) : "NULL", usb_cmdname(cmd), bool_str(ftdi), bool_str(readonce));
|
|
|
+
|
|
|
+ if (bufsiz > USB_MAX_READ)
|
|
|
+ quit(1, "%s USB read request %d too large (max=%d)", cgpu->drv->name, (int)bufsiz, USB_MAX_READ);
|
|
|
+
|
|
|
+ if (timeout == DEVTIMEOUT)
|
|
|
+ timeout = usbdev->found->timeout;
|
|
|
+
|
|
|
+ tot = usbdev->bufamt;
|
|
|
+ bufleft = bufsiz - tot;
|
|
|
+ if (tot)
|
|
|
+ cg_memcpy(usbbuf, usbdev->buffer, tot);
|
|
|
+ ptr = usbbuf + tot;
|
|
|
+ usbdev->bufamt = 0;
|
|
|
+
|
|
|
+ err = LIBUSB_SUCCESS;
|
|
|
+ if (end != NULL)
|
|
|
+ eom = strstr((const char *)usbbuf, end);
|
|
|
+
|
|
|
+ initial_timeout = timeout;
|
|
|
+ cgtime(&read_start);
|
|
|
+ tried_reset = 0;
|
|
|
+ while (bufleft > 0 && !eom) {
|
|
|
+ err = usb_perform_transfer(cgpu, usbdev, intinfo, epinfo, ptr, usbbufread,
|
|
|
+ &got, timeout, MODE_BULK_READ, cmd,
|
|
|
+ first ? SEQ0 : SEQ1, cancellable, false);
|
|
|
+ if (NODEV(err))
|
|
|
+ goto out_noerrmsg;
|
|
|
+
|
|
|
+ cgtime(&tv_finish);
|
|
|
+ ptr[got] = '\0';
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @_usb_read(%s (nodev=%s)) first=%s err=%d%s got=%d ptr='%s' usbbufread=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), bool_str(first), err, isnodev(err), got, (char *)str_text((char *)ptr), (int)usbbufread);
|
|
|
+
|
|
|
+ if (ftdi) {
|
|
|
+ // first 2 bytes returned are an FTDI status
|
|
|
+ if (got > 2) {
|
|
|
+ got -= 2;
|
|
|
+ memmove(ptr, ptr+2, got+1);
|
|
|
+ } else {
|
|
|
+ got = 0;
|
|
|
+ *ptr = '\0';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ tot += got;
|
|
|
+ if (end != NULL)
|
|
|
+ eom = strstr((const char *)usbbuf, end);
|
|
|
+
|
|
|
+ /* Attempt a usb reset for an error that will otherwise cause
|
|
|
+ * this device to drop out provided we know the device still
|
|
|
+ * might exist. */
|
|
|
+ if (err && err != LIBUSB_ERROR_TIMEOUT) {
|
|
|
+ applog(LOG_WARNING, "%s %i %s usb read err:(%d) %s", cgpu->drv->name,
|
|
|
+ cgpu->device_id, usb_cmdname(cmd), err, libusb_error_name(err));
|
|
|
+ if (err != LIBUSB_ERROR_NO_DEVICE && !tried_reset) {
|
|
|
+ err = libusb_reset_device(usbdev->handle);
|
|
|
+ tried_reset = 1; // don't call reset twice in a row
|
|
|
+ applog(LOG_WARNING, "%s %i attempted reset got err:(%d) %s",
|
|
|
+ cgpu->drv->name, cgpu->device_id, err, libusb_error_name(err));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ tried_reset = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NODEV(err))
|
|
|
+ goto out_noerrmsg;
|
|
|
+
|
|
|
+ ptr += got;
|
|
|
+ bufleft -= got;
|
|
|
+ if (bufleft < 1)
|
|
|
+ err = LIBUSB_SUCCESS;
|
|
|
+
|
|
|
+ if (err || readonce)
|
|
|
+ break;
|
|
|
+
|
|
|
+
|
|
|
+ first = false;
|
|
|
+
|
|
|
+ done = tdiff(&tv_finish, &read_start);
|
|
|
+ // N.B. this is: return last err with whatever size has already been read
|
|
|
+ timeout = initial_timeout - (done * 1000);
|
|
|
+ if (timeout <= 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If we found the end of message marker, just use that data and
|
|
|
+ * return success. */
|
|
|
+ if (eom) {
|
|
|
+ size_t eomlen = (void *)eom - (void *)usbbuf + endlen;
|
|
|
+
|
|
|
+ if (eomlen < bufsiz) {
|
|
|
+ bufsiz = eomlen;
|
|
|
+ err = LIBUSB_SUCCESS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // N.B. usbdev->buffer was emptied before the while() loop
|
|
|
+ if (tot > (int)bufsiz) {
|
|
|
+ usbdev->bufamt = tot - bufsiz;
|
|
|
+ cg_memcpy(usbdev->buffer, usbbuf + bufsiz, usbdev->bufamt);
|
|
|
+ tot -= usbdev->bufamt;
|
|
|
+ usbbuf[tot] = '\0';
|
|
|
+ applog(LOG_DEBUG, "USB: %s%i read1 buffering %d extra bytes",
|
|
|
+ cgpu->drv->name, cgpu->device_id, usbdev->bufamt);
|
|
|
+ }
|
|
|
+
|
|
|
+ *processed = tot;
|
|
|
+ cg_memcpy((char *)buf, (const char *)usbbuf, (tot < (int)bufsiz) ? tot + 1 : (int)bufsiz);
|
|
|
+
|
|
|
+out_noerrmsg:
|
|
|
+ if (NODEV(err)) {
|
|
|
+ cg_ruwlock(&cgpu->usbinfo.devlock);
|
|
|
+ release_cgpu(cgpu);
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+ } else
|
|
|
+ DEVRUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int _usb_write(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t bufsiz, int *processed, int timeout, enum usb_cmds cmd)
|
|
|
+{
|
|
|
+ struct timeval write_start, tv_finish;
|
|
|
+ struct cg_usb_device *usbdev;
|
|
|
+ unsigned int initial_timeout;
|
|
|
+ int err, sent, tot, pstate, tried_reset;
|
|
|
+ bool first = true;
|
|
|
+ double done;
|
|
|
+
|
|
|
+ DEVRLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: _usb_write(%s (nodev=%s),intinfo=%d,epinfo=%d,buf='%s',bufsiz=%d,proc=%p,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, (char *)str_text(buf), (int)bufsiz, processed, timeout, usb_cmdname(cmd));
|
|
|
+
|
|
|
+ *processed = 0;
|
|
|
+
|
|
|
+ if (cgpu->usbinfo.nodev) {
|
|
|
+ USB_REJECT(cgpu, MODE_BULK_WRITE);
|
|
|
+
|
|
|
+ err = LIBUSB_ERROR_NO_DEVICE;
|
|
|
+ goto out_noerrmsg;
|
|
|
+ }
|
|
|
+
|
|
|
+ usbdev = cgpu->usbdev;
|
|
|
+ if (timeout == DEVTIMEOUT)
|
|
|
+ timeout = usbdev->found->timeout;
|
|
|
+
|
|
|
+ tot = 0;
|
|
|
+ err = LIBUSB_SUCCESS;
|
|
|
+ initial_timeout = timeout;
|
|
|
+ cgtime(&write_start);
|
|
|
+ tried_reset = 0;
|
|
|
+ while (bufsiz > 0) {
|
|
|
+ int tosend = bufsiz;
|
|
|
+
|
|
|
+ /* USB 1.1 devices don't handle zero packets well so split them
|
|
|
+ * up to not have the final transfer equal to the wMaxPacketSize
|
|
|
+ * or they will stall waiting for more data. */
|
|
|
+ if (usbdev->usb11) {
|
|
|
+ struct usb_epinfo *ue = &usbdev->found->intinfos[intinfo].epinfos[epinfo];
|
|
|
+
|
|
|
+ if (tosend == ue->wMaxPacketSize) {
|
|
|
+ tosend >>= 1;
|
|
|
+ if (unlikely(!tosend))
|
|
|
+ tosend = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ err = usb_perform_transfer(cgpu, usbdev, intinfo, epinfo, (unsigned char *)buf,
|
|
|
+ tosend, &sent, timeout, MODE_BULK_WRITE,
|
|
|
+ cmd, first ? SEQ0 : SEQ1, false, usbdev->tt);
|
|
|
+ if (NODEV(err))
|
|
|
+ goto out_noerrmsg;
|
|
|
+
|
|
|
+ cgtime(&tv_finish);
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @_usb_write(%s (nodev=%s)) err=%d%s sent=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err), sent);
|
|
|
+
|
|
|
+ tot += sent;
|
|
|
+
|
|
|
+ /* Unlike reads, even a timeout error is unrecoverable on
|
|
|
+ * writes. */
|
|
|
+ if (err) {
|
|
|
+ applog(LOG_WARNING, "%s %i %s usb write err:(%d) %s", cgpu->drv->name,
|
|
|
+ cgpu->device_id, usb_cmdname(cmd), err, libusb_error_name(err));
|
|
|
+ if (err != LIBUSB_ERROR_NO_DEVICE && !tried_reset) {
|
|
|
+ err = libusb_reset_device(usbdev->handle);
|
|
|
+ tried_reset = 1; // don't try reset twice in a row
|
|
|
+ applog(LOG_WARNING, "%s %i attempted reset got err:(%d) %s",
|
|
|
+ cgpu->drv->name, cgpu->device_id, err, libusb_error_name(err));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ tried_reset = 0;
|
|
|
+ }
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+
|
|
|
+ buf += sent;
|
|
|
+ bufsiz -= sent;
|
|
|
+
|
|
|
+ first = false;
|
|
|
+
|
|
|
+ done = tdiff(&tv_finish, &write_start);
|
|
|
+ // N.B. this is: return last err with whatever size was written
|
|
|
+ timeout = initial_timeout - (done * 1000);
|
|
|
+ if (timeout <= 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ *processed = tot;
|
|
|
+
|
|
|
+out_noerrmsg:
|
|
|
+ if (NODEV(err)) {
|
|
|
+ cg_ruwlock(&cgpu->usbinfo.devlock);
|
|
|
+ release_cgpu(cgpu);
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+ } else
|
|
|
+ DEVRUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/* As we do for bulk reads, emulate a sync function for control transfers using
|
|
|
+ * our own timeouts that takes the same parameters as libusb_control_transfer.
|
|
|
+ */
|
|
|
+static int usb_control_transfer(struct cgpu_info *cgpu, libusb_device_handle *dev_handle, uint8_t bmRequestType,
|
|
|
+ uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
|
|
|
+ unsigned char *buffer, uint16_t wLength, unsigned int timeout)
|
|
|
+{
|
|
|
+ struct usb_transfer ut;
|
|
|
+ unsigned char buf[70];
|
|
|
+ int err, transferred;
|
|
|
+ bool tt = false;
|
|
|
+
|
|
|
+ if (unlikely(cgpu->shutdown))
|
|
|
+ return libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, buffer, wLength, timeout);
|
|
|
+
|
|
|
+ init_usb_transfer(&ut);
|
|
|
+ libusb_fill_control_setup(buf, bmRequestType, bRequest, wValue,
|
|
|
+ wIndex, wLength);
|
|
|
+ if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
|
|
|
+ if (wLength)
|
|
|
+ cg_memcpy(buf + LIBUSB_CONTROL_SETUP_SIZE, buffer, wLength);
|
|
|
+ if (cgpu->usbdev->descriptor->bcdUSB < 0x0200)
|
|
|
+ tt = true;
|
|
|
+ }
|
|
|
+ libusb_fill_control_transfer(ut.transfer, dev_handle, buf, transfer_callback,
|
|
|
+ &ut, 0);
|
|
|
+ err = usb_submit_transfer(&ut, ut.transfer, false, tt);
|
|
|
+ if (!err)
|
|
|
+ err = callback_wait(&ut, &transferred, timeout);
|
|
|
+ if (err == LIBUSB_SUCCESS && transferred) {
|
|
|
+ if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
|
|
|
+ cg_memcpy(buffer, libusb_control_transfer_get_data(ut.transfer),
|
|
|
+ transferred);
|
|
|
+ err = transferred;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ err = usb_transfer_toerr(err);
|
|
|
+out:
|
|
|
+ complete_usb_transfer(&ut);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int __usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, unsigned int timeout, __maybe_unused enum usb_cmds cmd)
|
|
|
+{
|
|
|
+ struct cg_usb_device *usbdev;
|
|
|
+#if DO_USB_STATS
|
|
|
+ struct timeval tv_start, tv_finish;
|
|
|
+#endif
|
|
|
+ unsigned char buf[64];
|
|
|
+ uint32_t *buf32 = (uint32_t *)buf;
|
|
|
+ int err, i, bufsiz;
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: _usb_transfer(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",siz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, siz, timeout, usb_cmdname(cmd));
|
|
|
+
|
|
|
+ if (cgpu->usbinfo.nodev) {
|
|
|
+ USB_REJECT(cgpu, MODE_CTRL_WRITE);
|
|
|
+
|
|
|
+ err = LIBUSB_ERROR_NO_DEVICE;
|
|
|
+ goto out_;
|
|
|
+ }
|
|
|
+ usbdev = cgpu->usbdev;
|
|
|
+ if (timeout == DEVTIMEOUT)
|
|
|
+ timeout = usbdev->found->timeout;
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @_usb_transfer() data=%s", bin2hex((unsigned char *)data, (size_t)siz));
|
|
|
+
|
|
|
+ if (siz > 0) {
|
|
|
+ bufsiz = siz - 1;
|
|
|
+ bufsiz >>= 2;
|
|
|
+ bufsiz++;
|
|
|
+ for (i = 0; i < bufsiz; i++)
|
|
|
+ buf32[i] = htole32(data[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @_usb_transfer() buf=%s", bin2hex(buf, (size_t)siz));
|
|
|
+
|
|
|
+ STATS_TIMEVAL(&tv_start);
|
|
|
+ err = usb_control_transfer(cgpu, usbdev->handle, request_type, bRequest,
|
|
|
+ wValue, wIndex, buf, (uint16_t)siz, timeout);
|
|
|
+ STATS_TIMEVAL(&tv_finish);
|
|
|
+ USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_WRITE, cmd, SEQ0, timeout);
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @_usb_transfer(%s (nodev=%s)) err=%d%s", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err));
|
|
|
+
|
|
|
+ if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
|
|
|
+ applog(LOG_WARNING, "%s %i usb transfer err:(%d) %s", cgpu->drv->name, cgpu->device_id,
|
|
|
+ err, libusb_error_name(err));
|
|
|
+ }
|
|
|
+out_:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/* We use the write devlock for control transfers since some control transfers
|
|
|
+ * are rare but may be changing settings within the device causing problems
|
|
|
+ * if concurrent transfers are happening. Using the write lock serialises
|
|
|
+ * any transfers. */
|
|
|
+int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, unsigned int timeout, enum usb_cmds cmd)
|
|
|
+{
|
|
|
+ int pstate, err;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ err = __usb_transfer(cgpu, request_type, bRequest, wValue, wIndex, data, siz, timeout, cmd);
|
|
|
+
|
|
|
+ if (NOCONTROLDEV(err))
|
|
|
+ release_cgpu(cgpu);
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, char *buf, int bufsiz, int *amount, unsigned int timeout, __maybe_unused enum usb_cmds cmd)
|
|
|
+{
|
|
|
+ struct cg_usb_device *usbdev;
|
|
|
+#if DO_USB_STATS
|
|
|
+ struct timeval tv_start, tv_finish;
|
|
|
+#endif
|
|
|
+ unsigned char tbuf[64];
|
|
|
+ int err, pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: _usb_transfer_read(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",bufsiz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, bufsiz, timeout, usb_cmdname(cmd));
|
|
|
+
|
|
|
+ if (cgpu->usbinfo.nodev) {
|
|
|
+ USB_REJECT(cgpu, MODE_CTRL_READ);
|
|
|
+
|
|
|
+ err = LIBUSB_ERROR_NO_DEVICE;
|
|
|
+ goto out_noerrmsg;
|
|
|
+ }
|
|
|
+ usbdev = cgpu->usbdev;
|
|
|
+ if (timeout == DEVTIMEOUT)
|
|
|
+ timeout = usbdev->found->timeout;
|
|
|
+
|
|
|
+ *amount = 0;
|
|
|
+
|
|
|
+ memset(tbuf, 0, 64);
|
|
|
+ STATS_TIMEVAL(&tv_start);
|
|
|
+ err = usb_control_transfer(cgpu, usbdev->handle, request_type, bRequest,
|
|
|
+ wValue, wIndex, tbuf, (uint16_t)bufsiz, timeout);
|
|
|
+ STATS_TIMEVAL(&tv_finish);
|
|
|
+ USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_READ, cmd, SEQ0, timeout);
|
|
|
+ cg_memcpy(buf, tbuf, bufsiz);
|
|
|
+
|
|
|
+ USBDEBUG("USB debug: @_usb_transfer_read(%s (nodev=%s)) amt/err=%d%s%s%s", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err), err > 0 ? " = " : BLANK, err > 0 ? bin2hex((unsigned char *)buf, (size_t)err) : BLANK);
|
|
|
+
|
|
|
+ if (err > 0) {
|
|
|
+ *amount = err;
|
|
|
+ err = 0;
|
|
|
+ }
|
|
|
+ if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
|
|
|
+ applog(LOG_WARNING, "%s %i usb transfer read err:(%d) %s", cgpu->drv->name, cgpu->device_id,
|
|
|
+ err, libusb_error_name(err));
|
|
|
+ }
|
|
|
+out_noerrmsg:
|
|
|
+ if (NOCONTROLDEV(err))
|
|
|
+ release_cgpu(cgpu);
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+#define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)
|
|
|
+#define FTDI_RS0_CTS (1 << 4)
|
|
|
+#define FTDI_RS0_DSR (1 << 5)
|
|
|
+#define FTDI_RS0_RI (1 << 6)
|
|
|
+#define FTDI_RS0_RLSD (1 << 7)
|
|
|
+
|
|
|
+/* Clear to send for FTDI */
|
|
|
+int usb_ftdi_cts(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ char buf[2], ret;
|
|
|
+ int err, amount;
|
|
|
+
|
|
|
+ err = _usb_transfer_read(cgpu, (uint8_t)FTDI_TYPE_IN, (uint8_t)5,
|
|
|
+ (uint16_t)0, (uint16_t)0, buf, 2,
|
|
|
+ &amount, DEVTIMEOUT, C_FTDI_STATUS);
|
|
|
+ /* We return true in case drivers are waiting indefinitely to try and
|
|
|
+ * write to something that's not there. */
|
|
|
+ if (err)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ ret = buf[0] & FTDI_STATUS_B0_MASK;
|
|
|
+ return (ret & FTDI_RS0_CTS);
|
|
|
+}
|
|
|
+
|
|
|
+int _usb_ftdi_set_latency(struct cgpu_info *cgpu, int intinfo)
|
|
|
+{
|
|
|
+ int err = 0;
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ if (cgpu->usbdev) {
|
|
|
+ if (cgpu->usbdev->usb_type != USB_TYPE_FTDI) {
|
|
|
+ applog(LOG_ERR, "%s: cgid %d latency request on non-FTDI device",
|
|
|
+ cgpu->drv->name, cgpu->cgminer_id);
|
|
|
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
|
|
|
+ } else if (cgpu->usbdev->found->latency == LATENCY_UNUSED) {
|
|
|
+ applog(LOG_ERR, "%s: cgid %d invalid latency (UNUSED)",
|
|
|
+ cgpu->drv->name, cgpu->cgminer_id);
|
|
|
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!err)
|
|
|
+ err = __usb_transfer(cgpu, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY,
|
|
|
+ cgpu->usbdev->found->latency,
|
|
|
+ USBIF(cgpu->usbdev, intinfo),
|
|
|
+ NULL, 0, DEVTIMEOUT, C_LATENCY);
|
|
|
+ }
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "%s: cgid %d %s got err %d",
|
|
|
+ cgpu->drv->name, cgpu->cgminer_id,
|
|
|
+ usb_cmdname(C_LATENCY), err);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+void usb_buffer_clear(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ if (cgpu->usbdev)
|
|
|
+ cgpu->usbdev->bufamt = 0;
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t usb_buffer_size(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ uint32_t ret = 0;
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVRLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ if (cgpu->usbdev)
|
|
|
+ ret = cgpu->usbdev->bufamt;
|
|
|
+
|
|
|
+ DEVRUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * The value returned (0) when usbdev is NULL
|
|
|
+ * doesn't matter since it also means the next call to
|
|
|
+ * any usbutils function will fail with a nodev
|
|
|
+ * N.B. this is to get the interface number to use in a control_transfer
|
|
|
+ * which for some devices isn't actually the interface number
|
|
|
+ */
|
|
|
+int _usb_interface(struct cgpu_info *cgpu, int intinfo)
|
|
|
+{
|
|
|
+ int interface = 0;
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVRLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ if (cgpu->usbdev)
|
|
|
+ interface = cgpu->usbdev->found->intinfos[intinfo].ctrl_transfer;
|
|
|
+
|
|
|
+ DEVRUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return interface;
|
|
|
+}
|
|
|
+
|
|
|
+enum sub_ident usb_ident(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ enum sub_ident ident = IDENT_UNK;
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVRLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ if (cgpu->usbdev)
|
|
|
+ ident = cgpu->usbdev->ident;
|
|
|
+
|
|
|
+ DEVRUNLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ return ident;
|
|
|
+}
|
|
|
+
|
|
|
+// Need to set all devices with matching usbdev
|
|
|
+void usb_set_dev_start(struct cgpu_info *cgpu)
|
|
|
+{
|
|
|
+ struct cg_usb_device *cgusb;
|
|
|
+ struct cgpu_info *cgpu2;
|
|
|
+ struct timeval now;
|
|
|
+ int pstate;
|
|
|
+
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+
|
|
|
+ cgusb = cgpu->usbdev;
|
|
|
+
|
|
|
+ // If the device wasn't dropped
|
|
|
+ if (cgusb != NULL) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ cgtime(&now);
|
|
|
+
|
|
|
+ for (i = 0; i < total_devices; i++) {
|
|
|
+ cgpu2 = get_devices(i);
|
|
|
+ if (cgpu2->usbdev == cgusb)
|
|
|
+ copy_time(&(cgpu2->dev_start_tv), &now);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+}
|
|
|
+
|
|
|
+void usb_cleanup(void)
|
|
|
+{
|
|
|
+ struct cgpu_info *cgpu;
|
|
|
+ int count, pstate;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ hotplug_time = 0;
|
|
|
+
|
|
|
+ cgsleep_ms(10);
|
|
|
+
|
|
|
+ count = 0;
|
|
|
+ for (i = 0; i < total_devices; i++) {
|
|
|
+ cgpu = devices[i];
|
|
|
+ switch (cgpu->drv->drv_id) {
|
|
|
+ case DRIVER_bflsc:
|
|
|
+ case DRIVER_bitforce:
|
|
|
+ case DRIVER_bitfury:
|
|
|
+ case DRIVER_cointerra:
|
|
|
+ case DRIVER_drillbit:
|
|
|
+ case DRIVER_gekko:
|
|
|
+ case DRIVER_modminer:
|
|
|
+ case DRIVER_icarus:
|
|
|
+ case DRIVER_avalon:
|
|
|
+ case DRIVER_avalon2:
|
|
|
+ case DRIVER_avalon4:
|
|
|
+ case DRIVER_avalon7:
|
|
|
+ case DRIVER_avalon8:
|
|
|
+ case DRIVER_avalon9:
|
|
|
+ case DRIVER_avalonlc3:
|
|
|
+ case DRIVER_avalonm:
|
|
|
+ case DRIVER_klondike:
|
|
|
+ case DRIVER_hashfast:
|
|
|
+ DEVWLOCK(cgpu, pstate);
|
|
|
+ release_cgpu(cgpu);
|
|
|
+ DEVWUNLOCK(cgpu, pstate);
|
|
|
+ count++;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Must attempt to wait for the resource thread to release coz
|
|
|
+ * during a restart it won't automatically release them in linux
|
|
|
+ */
|
|
|
+ if (count) {
|
|
|
+ struct timeval start, now;
|
|
|
+
|
|
|
+ cgtime(&start);
|
|
|
+ while (42) {
|
|
|
+ cgsleep_ms(50);
|
|
|
+
|
|
|
+ mutex_lock(&cgusbres_lock);
|
|
|
+
|
|
|
+ if (!res_work_head)
|
|
|
+ break;
|
|
|
+
|
|
|
+ cgtime(&now);
|
|
|
+ if (tdiff(&now, &start) > 0.366) {
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "usb_cleanup gave up waiting for resource thread");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+ }
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ cgsem_destroy(&usb_resource_sem);
|
|
|
+}
|
|
|
+
|
|
|
+#define DRIVER_COUNT_FOUND(X) if (X##_drv.name && strcasecmp(ptr, X##_drv.name) == 0) { \
|
|
|
+ drv_count[X##_drv.drv_id].limit = lim; \
|
|
|
+ found = true; \
|
|
|
+ }
|
|
|
+void usb_initialise(void)
|
|
|
+{
|
|
|
+ char *fre, *ptr, *comma, *colon;
|
|
|
+ int bus, dev, lim, i;
|
|
|
+ bool found;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&ut_list);
|
|
|
+
|
|
|
+ for (i = 0; i < DRIVER_MAX; i++) {
|
|
|
+ drv_count[i].count = 0;
|
|
|
+ drv_count[i].limit = 999999;
|
|
|
+ }
|
|
|
+
|
|
|
+ cgusb_check_init();
|
|
|
+
|
|
|
+ if (opt_usb_select && *opt_usb_select) {
|
|
|
+ // Absolute device limit
|
|
|
+ if (*opt_usb_select == ':') {
|
|
|
+ total_limit = atoi(opt_usb_select+1);
|
|
|
+ if (total_limit < 0)
|
|
|
+ quit(1, "Invalid --usb total limit");
|
|
|
+ // Comma list of bus:dev devices to match
|
|
|
+ } else if (isdigit(*opt_usb_select)) {
|
|
|
+ fre = ptr = strdup(opt_usb_select);
|
|
|
+ do {
|
|
|
+ comma = strchr(ptr, ',');
|
|
|
+ if (comma)
|
|
|
+ *(comma++) = '\0';
|
|
|
+
|
|
|
+ colon = strchr(ptr, ':');
|
|
|
+ if (!colon)
|
|
|
+ quit(1, "Invalid --usb bus:dev missing ':'");
|
|
|
+
|
|
|
+ *(colon++) = '\0';
|
|
|
+
|
|
|
+ if (!isdigit(*ptr))
|
|
|
+ quit(1, "Invalid --usb bus:dev - bus must be a number");
|
|
|
+
|
|
|
+ if (!isdigit(*colon) && *colon != '*')
|
|
|
+ quit(1, "Invalid --usb bus:dev - dev must be a number or '*'");
|
|
|
+
|
|
|
+ bus = atoi(ptr);
|
|
|
+ if (bus <= 0)
|
|
|
+ quit(1, "Invalid --usb bus:dev - bus must be > 0");
|
|
|
+
|
|
|
+ if (*colon == '*')
|
|
|
+ dev = -1;
|
|
|
+ else {
|
|
|
+ dev = atoi(colon);
|
|
|
+ if (dev <= 0)
|
|
|
+ quit(1, "Invalid --usb bus:dev - dev must be > 0 or '*'");
|
|
|
+ }
|
|
|
+
|
|
|
+ busdev = cgrealloc(busdev, sizeof(*busdev) * (++busdev_count));
|
|
|
+ busdev[busdev_count-1].bus_number = bus;
|
|
|
+ busdev[busdev_count-1].device_address = dev;
|
|
|
+
|
|
|
+ ptr = comma;
|
|
|
+ } while (ptr);
|
|
|
+ free(fre);
|
|
|
+ // Comma list of DRV:limit
|
|
|
+ } else {
|
|
|
+ fre = ptr = strdup(opt_usb_select);
|
|
|
+ do {
|
|
|
+ comma = strchr(ptr, ',');
|
|
|
+ if (comma)
|
|
|
+ *(comma++) = '\0';
|
|
|
+
|
|
|
+ colon = strchr(ptr, ':');
|
|
|
+ if (!colon)
|
|
|
+ quit(1, "Invalid --usb DRV:limit missing ':'");
|
|
|
+
|
|
|
+ *(colon++) = '\0';
|
|
|
+
|
|
|
+ if (!isdigit(*colon))
|
|
|
+ quit(1, "Invalid --usb DRV:limit - limit must be a number");
|
|
|
+
|
|
|
+ lim = atoi(colon);
|
|
|
+ if (lim < 0)
|
|
|
+ quit(1, "Invalid --usb DRV:limit - limit must be >= 0");
|
|
|
+
|
|
|
+ found = false;
|
|
|
+ /* Use the DRIVER_PARSE_COMMANDS macro to iterate
|
|
|
+ * over all the drivers. */
|
|
|
+ DRIVER_PARSE_COMMANDS(DRIVER_COUNT_FOUND)
|
|
|
+ if (!found)
|
|
|
+ quit(1, "Invalid --usb DRV:limit - unknown DRV='%s'", ptr);
|
|
|
+
|
|
|
+ ptr = comma;
|
|
|
+ } while (ptr);
|
|
|
+ free(fre);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef WIN32
|
|
|
+#include <errno.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <sys/file.h>
|
|
|
+#include <fcntl.h>
|
|
|
+
|
|
|
+#ifndef __APPLE__
|
|
|
+union semun {
|
|
|
+ int val;
|
|
|
+ struct semid_ds *buf;
|
|
|
+ unsigned short *array;
|
|
|
+ struct seminfo *__buf;
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#else
|
|
|
+static LPSECURITY_ATTRIBUTES unsec(LPSECURITY_ATTRIBUTES sec)
|
|
|
+{
|
|
|
+ SECURITY_DESCRIPTOR *sd = (PSECURITY_DESCRIPTOR)(sec->lpSecurityDescriptor);
|
|
|
+ FreeSid(sd->Group);
|
|
|
+ free(sec->lpSecurityDescriptor);
|
|
|
+ free(sec);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static LPSECURITY_ATTRIBUTES mksec(const char *dname, uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld = {SECURITY_WORLD_SID_AUTHORITY};
|
|
|
+ PSID gsid = NULL;
|
|
|
+ LPSECURITY_ATTRIBUTES sec_att = NULL;
|
|
|
+ PSECURITY_DESCRIPTOR sec_des = NULL;
|
|
|
+
|
|
|
+ sec_des = cgmalloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
|
+
|
|
|
+ if (!InitializeSecurityDescriptor(sec_des, SECURITY_DESCRIPTOR_REVISION)) {
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s (%d:%d) USB failed to init secdes err (%d)",
|
|
|
+ dname, (int)bus_number, (int)device_address,
|
|
|
+ (int)GetLastError());
|
|
|
+ free(sec_des);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!SetSecurityDescriptorDacl(sec_des, TRUE, NULL, FALSE)) {
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s (%d:%d) USB failed to secdes dacl err (%d)",
|
|
|
+ dname, (int)bus_number, (int)device_address,
|
|
|
+ (int)GetLastError());
|
|
|
+ free(sec_des);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &gsid)) {
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s (%d:%d) USB failed to create gsid err (%d)",
|
|
|
+ dname, (int)bus_number, (int)device_address,
|
|
|
+ (int)GetLastError());
|
|
|
+ free(sec_des);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!SetSecurityDescriptorGroup(sec_des, gsid, FALSE)) {
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s (%d:%d) USB failed to secdes grp err (%d)",
|
|
|
+ dname, (int)bus_number, (int)device_address,
|
|
|
+ (int)GetLastError());
|
|
|
+ FreeSid(gsid);
|
|
|
+ free(sec_des);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ sec_att = cgmalloc(sizeof(*sec_att));
|
|
|
+
|
|
|
+ sec_att->nLength = sizeof(*sec_att);
|
|
|
+ sec_att->lpSecurityDescriptor = sec_des;
|
|
|
+ sec_att->bInheritHandle = FALSE;
|
|
|
+
|
|
|
+ return sec_att;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+// Any errors should always be printed since they will rarely if ever occur
|
|
|
+// and thus it is best to always display them
|
|
|
+static bool resource_lock(const char *dname, uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ applog(LOG_DEBUG, "USB res lock %s %d-%d", dname, (int)bus_number, (int)device_address);
|
|
|
+#ifdef WIN32
|
|
|
+ struct cgpu_info *cgpu;
|
|
|
+ LPSECURITY_ATTRIBUTES sec;
|
|
|
+ HANDLE usbMutex;
|
|
|
+ char name[64];
|
|
|
+ DWORD res;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (is_in_use_bd(bus_number, device_address))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ snprintf(name, sizeof(name), "cg-usb-%d-%d", (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ sec = mksec(dname, bus_number, device_address);
|
|
|
+ if (!sec)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ usbMutex = CreateMutex(sec, FALSE, name);
|
|
|
+ if (usbMutex == NULL) {
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s USB failed to get '%s' err (%d)",
|
|
|
+ dname, name, (int)GetLastError());
|
|
|
+ sec = unsec(sec);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ res = WaitForSingleObject(usbMutex, 0);
|
|
|
+
|
|
|
+ switch(res) {
|
|
|
+ case WAIT_OBJECT_0:
|
|
|
+ case WAIT_ABANDONED:
|
|
|
+ // Am I using it already?
|
|
|
+ for (i = 0; i < total_devices; i++) {
|
|
|
+ cgpu = get_devices(i);
|
|
|
+ if (cgpu->usbinfo.bus_number == bus_number &&
|
|
|
+ cgpu->usbinfo.device_address == device_address &&
|
|
|
+ cgpu->usbinfo.nodev == false) {
|
|
|
+ if (ReleaseMutex(usbMutex)) {
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "MTX: %s USB can't get '%s' - device in use",
|
|
|
+ dname, name);
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s USB can't get '%s' - device in use - failure (%d)",
|
|
|
+ dname, name, (int)GetLastError());
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case WAIT_TIMEOUT:
|
|
|
+ if (!hotplug_mode)
|
|
|
+ applog(LOG_WARNING,
|
|
|
+ "MTX: %s USB failed to get '%s' - device in use",
|
|
|
+ dname, name);
|
|
|
+ goto fail;
|
|
|
+ case WAIT_FAILED:
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s USB failed to get '%s' err (%d)",
|
|
|
+ dname, name, (int)GetLastError());
|
|
|
+ goto fail;
|
|
|
+ default:
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s USB failed to get '%s' unknown reply (%d)",
|
|
|
+ dname, name, (int)res);
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ add_in_use(bus_number, device_address, false);
|
|
|
+ in_use_store_ress(bus_number, device_address, (void *)usbMutex, (void *)sec);
|
|
|
+
|
|
|
+ return true;
|
|
|
+fail:
|
|
|
+ CloseHandle(usbMutex);
|
|
|
+ sec = unsec(sec);
|
|
|
+ return false;
|
|
|
+#else
|
|
|
+ char name[64];
|
|
|
+ int fd;
|
|
|
+
|
|
|
+ if (is_in_use_bd(bus_number, device_address))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ snprintf(name, sizeof(name), "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address);
|
|
|
+ fd = open(name, O_CREAT|O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
|
+ if (fd == -1) {
|
|
|
+ applog(LOG_ERR, "%s USB open failed '%s' err (%d) %s",
|
|
|
+ dname, name, errno, strerror(errno));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (flock(fd, LOCK_EX | LOCK_NB)) {
|
|
|
+ applog(LOG_INFO, "%s USB failed to get '%s' - device in use",
|
|
|
+ dname, name);
|
|
|
+ close(fd);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ add_in_use(bus_number, device_address, false);
|
|
|
+ in_use_store_fd(bus_number, device_address, fd);
|
|
|
+ return true;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+// Any errors should always be printed since they will rarely if ever occur
|
|
|
+// and thus it is best to always display them
|
|
|
+static void resource_unlock(const char *dname, uint8_t bus_number, uint8_t device_address)
|
|
|
+{
|
|
|
+ applog(LOG_DEBUG, "USB res unlock %s %d-%d", dname, (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+#ifdef WIN32
|
|
|
+ LPSECURITY_ATTRIBUTES sec = NULL;
|
|
|
+ HANDLE usbMutex = NULL;
|
|
|
+ char name[64];
|
|
|
+
|
|
|
+ snprintf(name, sizeof(name), "cg-usb-%d-%d", (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ in_use_get_ress(bus_number, device_address, (void **)(&usbMutex), (void **)(&sec));
|
|
|
+
|
|
|
+ if (!usbMutex || !sec)
|
|
|
+ goto fila;
|
|
|
+
|
|
|
+ if (!ReleaseMutex(usbMutex))
|
|
|
+ applog(LOG_ERR,
|
|
|
+ "MTX: %s USB failed to release '%s' err (%d)",
|
|
|
+ dname, name, (int)GetLastError());
|
|
|
+
|
|
|
+fila:
|
|
|
+
|
|
|
+ if (usbMutex)
|
|
|
+ CloseHandle(usbMutex);
|
|
|
+ if (sec)
|
|
|
+ unsec(sec);
|
|
|
+ remove_in_use(bus_number, device_address);
|
|
|
+ return;
|
|
|
+#else
|
|
|
+ char name[64];
|
|
|
+ int fd;
|
|
|
+
|
|
|
+ snprintf(name, sizeof(name), "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address);
|
|
|
+
|
|
|
+ fd = in_use_get_fd(bus_number, device_address);
|
|
|
+ if (fd < 0)
|
|
|
+ return;
|
|
|
+ remove_in_use(bus_number, device_address);
|
|
|
+ close(fd);
|
|
|
+ unlink(name);
|
|
|
+ return;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static void resource_process()
|
|
|
+{
|
|
|
+ struct resource_work *res_work = NULL;
|
|
|
+ struct resource_reply *res_reply = NULL;
|
|
|
+ bool ok;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "RES: %s (%d:%d) lock=%d",
|
|
|
+ res_work_head->dname,
|
|
|
+ (int)res_work_head->bus_number,
|
|
|
+ (int)res_work_head->device_address,
|
|
|
+ res_work_head->lock);
|
|
|
+
|
|
|
+ if (res_work_head->lock) {
|
|
|
+ ok = resource_lock(res_work_head->dname,
|
|
|
+ res_work_head->bus_number,
|
|
|
+ res_work_head->device_address);
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "RES: %s (%d:%d) lock ok=%d",
|
|
|
+ res_work_head->dname,
|
|
|
+ (int)res_work_head->bus_number,
|
|
|
+ (int)res_work_head->device_address,
|
|
|
+ ok);
|
|
|
+
|
|
|
+ res_reply = cgcalloc(1, sizeof(*res_reply));
|
|
|
+
|
|
|
+ res_reply->bus_number = res_work_head->bus_number;
|
|
|
+ res_reply->device_address = res_work_head->device_address;
|
|
|
+ res_reply->got = ok;
|
|
|
+ res_reply->next = res_reply_head;
|
|
|
+
|
|
|
+ res_reply_head = res_reply;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ resource_unlock(res_work_head->dname,
|
|
|
+ res_work_head->bus_number,
|
|
|
+ res_work_head->device_address);
|
|
|
+
|
|
|
+ res_work = res_work_head;
|
|
|
+ res_work_head = res_work_head->next;
|
|
|
+ free(res_work);
|
|
|
+}
|
|
|
+
|
|
|
+void *usb_resource_thread(void __maybe_unused *userdata)
|
|
|
+{
|
|
|
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
|
|
+
|
|
|
+ RenameThread("USBResource");
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "RES: thread starting");
|
|
|
+
|
|
|
+ while (42) {
|
|
|
+ /* Wait to be told we have work to do */
|
|
|
+ cgsem_wait(&usb_resource_sem);
|
|
|
+
|
|
|
+ mutex_lock(&cgusbres_lock);
|
|
|
+ while (res_work_head)
|
|
|
+ resource_process();
|
|
|
+ mutex_unlock(&cgusbres_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void initialise_usblocks(void)
|
|
|
+{
|
|
|
+ mutex_init(&cgusb_lock);
|
|
|
+ mutex_init(&cgusbres_lock);
|
|
|
+ cglock_init(&cgusb_fd_lock);
|
|
|
+}
|