diff --git a/01-cgminer.rules b/01-cgminer.rules index 010dba81..f3a488a1 100644 --- a/01-cgminer.rules +++ b/01-cgminer.rules @@ -1,3 +1,20 @@ # Butterfly Labs FPGA and ASIC devices ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# ModMinerQuad ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Lancelot and Avalon +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Icarus +ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# AsicminerUSB +ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Cairnsmore1 +ATTRS{idVendor}=="067b", ATTRS{idProduct}=="0230", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Ztex +ATTRS{idVendor}=="221a", ATTRS{idProduct}=="0100", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" diff --git a/ASIC-README b/ASIC-README index 8796a792..9540a124 100644 --- a/ASIC-README +++ b/ASIC-README @@ -44,17 +44,12 @@ If your distribution does not have the plugdev group you can create it with: sudo groupadd plugdev In order for the BFL devices to instantly be owned by the plugdev group and -accessible by anyone from the plugdev group you can either copy the file +accessible by anyone from the plugdev group you can copy the file "01-cgminer.rules" from the cgminer archive into the /etc/udev/rules.d directory with the following command: sudo cp 01-cgminer.rules /etc/udev/rules.d/ -Or you can manually create a file/add to a rules.d file with following rules -(most users won't want to do this manually): -ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" -ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" - After this you can either manually restart udev and re-login, or more easily just reboot. @@ -64,11 +59,52 @@ AVALON DEVICES Currently all known Avalon devices come with their own operating system and a preinstalled version of cgminer as part of the flash firmware, based on the most current cgminer version so no configuration should be necessary. It is -possible to plug a USB cable from a PC into the Avalon device and use the ---avalon-options copying the command as used by the internal router used by the -Avalon. However since the Avalon code still currently uses the old serial usb -interface and is being rewritten to use direct USB, it is prudent to not be -dependent on this command long term, assuming it will go away. +possible to plug a USB cable from a PC into the Avalon device and mine using +cgminer as per any other device. It will autodetect and hotplug using default +options. You can customise the avalon behaviour by using the --avalon-options +command. + +eg: +--avalon-options 115200:24:10:45:282 + +The values are baud : miners : asic count : timeout : frequency. + +Baud: +The device is pretty much hard coded to emulate 115200 baud so you shouldn't +change this. + +Miners: +Most Avalons are 3 module devices, which come to 24 miners. 4 module devices +would use 32 here. + +Asic count: +Virtually all have 10, so don't change this. + +Timeout: +This is how long the device will work on a work item before accepting new work +to replace it. It should be changed according to the frequency (last setting). +It is possible to set this a little lower if you are trying to tune for short +block mining (eg p2pool) but much lower and the device will start creating +duplicate shares. + +Sample settings for different frequencies (last 2 values): +43:300 +45:282 (default) +47:270 +50:256 + +Frequency: +This is the clock speed of the devices. Only specific values work, 256, 270, +282 (default) and 300. + +If you use the full curses based interface with Avalons you will get this +information: +AVA 0: 22/ 46C 60%/2400R + +The values are: +ambient temp / highest device temp set fan % / lowest detected fan RPM. + +Use the API for more detailed information than this. --- diff --git a/FPGA-README b/FPGA-README index 4dfed2ab..98730b08 100644 --- a/FPGA-README +++ b/FPGA-README @@ -2,16 +2,14 @@ This README contains extended details about FPGA mining with cgminer -For ModMinerQuad (MMQ) and BitForce (BFL) ------------------------------------------ +For ModMinerQuad (MMQ) BitForce (BFL) and Icarus (ICA, BLT, LLT, AMU, CMR) +-------------------------------------------------------------------------- When mining on windows, the driver being used will determine if mining will work. If the driver doesn't allow mining, you will get a "USB init," error message i.e. one of: open device failed, err %d, you need to install a Windows USB driver for the device -or - kernel detach failed :( or claim interface %d failed, err %d @@ -32,19 +30,16 @@ with cgminer (not the libusbx version) When mining on linux, but not using 'sudo' and not logged into 'root' you may get a USB priviledge error (-3), so you may also need to do the following: -Create /etc/udev/rules.d/01-cgminer.rules -With: - ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" - ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + sudo cp 01-cgminer.rules /etc/udev/rules.d/ And also: sudo usermod -G plugdev -a `whoami` -Then reboot ... - If your linux distro doesn't have the 'plugdev' group, you can create it like: sudo groupadd plugdev +Then reboot ... + - There is a hidden option in cgminer to dump out a lot of information @@ -53,8 +48,7 @@ problems: --usb-dump 0 -It will only help if you have a working MMQ or BFL device attached to the -computer +It will only help if you have a working FPGA device listed above ModMinerQuad (MMQ) @@ -190,8 +184,8 @@ the MH/s value reported with the changed firmware - and the MH/s reported will be less than the firmware speed since you lose work on every block change. -Icarus (ICA) ------------- +Icarus (ICA, BLT, LLT, AMU, CMR) +-------------------------------- There are two hidden options in cgminer when Icarus support is compiled in: @@ -245,8 +239,8 @@ scan hash time, for the first 5 nonce's or one minute (whichever is longer) In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default value or the value specified -The optional additional =N specifies to set the default abort at N 1/10ths of a second, not the -calculated value, which is 112 for 2.6316ns +The optional additional =N specifies to set the default abort at N * 100ms, not the calculated +value, which is ~112 for 2.6316ns To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use @@ -258,6 +252,9 @@ Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s If an FPGA device does hash faster than ~840MH/s it should work correctly if you supply the correct hash time nanoseconds value +The Icarus code will automatically detect Icarus, Lancelot, AsicminerUSB and Cairnsmore1 +FPGA devices and set default settings to match those devices if you don't specify them + The timing code itself will affect the Icarus performance since it increases the delay after work is completed or aborted until it starts again The increase is, however, extremely small and the actual increase is reported with the diff --git a/Makefile.am b/Makefile.am index ccd4e6b6..b9487ee4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,7 +25,7 @@ bin_SCRIPTS = $(top_srcdir)/*.cl cgminer_LDFLAGS = $(PTHREAD_FLAGS) cgminer_LDADD = $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \ @OPENCL_LIBS@ @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \ - @UDEV_LIBS@ @LIBUSB_LIBS@ @MM_LIBS@ \ + @LIBUSB_LIBS@ @MM_LIBS@ \ @MATH_LIBS@ lib/libgnu.a ccan/libccan.a if HAVE_WINDOWS diff --git a/README b/README index bda7fd86..8a54bd5b 100644 --- a/README +++ b/README @@ -111,7 +111,7 @@ CGMiner specific configuration options: --disable-adl Override detection and disable building with adl --enable-bflsc Compile support for BFL ASICs (default disabled) --enable-bitforce Compile support for BitForce FPGAs(default disabled) - --enable-icarus Compile support for Icarus Board(default disabled) + --enable-icarus Compile support for Icarus bitstream FPGAs(default disabled) --enable-modminer Compile support for ModMiner FPGAs(default disabled) --enable-ztex Compile support for Ztex Board(default disabled) --enable-avalon Compile support for Avalon (default disabled) @@ -235,13 +235,14 @@ ASIC and FPGA mining boards (BFL ASIC, BitForce, Icarus, ModMiner, Ztex) only options: Cgminer will automatically find all of your BFL ASIC, BitForce FPGAs, -ModMiner FPGAs or Ztex FPGAs -The --usb option can restrict how many BFL ASIC, BitForce FPGAs or -ModMiner FPGAs it finds: +ModMiner FPGAs, Icarus bitstream FPGAs or Ztex FPGAs + +The --usb option can restrict how many BFL ASIC, BitForce FPGAs, +ModMiner FPGAs or Icarus bitstream FPGAs it finds: --usb 1:2,1:3,1:4,1:* or - --usb BAS:1,BFL:1,MMQ:0 + --usb BAS:1,BFL:1,MMQ:0,ICA:0 or --usb :10 @@ -260,15 +261,16 @@ This is useful if you unplug a device then plug it back in the same port, it usually reappears with the same bus_number but a different device_address You can see the list of all USB devices on linux with 'sudo lsusb' -Cgminer will list the recognised USB devices with the '--usb-dump 0' option +Cgminer will list the recognised USB devices with the '-n' option or the +'--usb-dump 0' option The '--usb-dump N' option with a value of N greater than 0 will dump a lot of details about each recognised USB device If you wish to see all USB devices, include the --usb-list-all option The second version - --usb BAS:1,BFL:1,MMQ:0 + --usb BAS:1,BFL:1,MMQ:0,ICA:0 allows you to specify how many devices to choose based on each device -driver cgminer has - there are currently 3 USB drivers: BAS, BFL & MMQ +driver cgminer has - there are currently 4 USB drivers: BAS, BFL, MMQ & ICA N.B. you can only specify which device driver to limit, not the type of each device, e.g. with BAS:n you can limit how many BFL ASIC devices will be checked, but you cannot limit the number of each type of BFL ASIC @@ -283,28 +285,8 @@ not scan for any more If one of the 10 devices stops working, hotplug - if enabled, as is default - will scan normally again until it has 10 devices + --usb :0 will disable all USB I/O other than to initialise libusb ---scan-serial|-S Serial port to probe for Icarus mining device - -This option is only for Icarus bitstream FPGAs - -By default, cgminer will scan for autodetected Icarus unless at least one --S is specified for that driver. If you specify -S and still want cgminer -to scan, you must also use "-S auto". If you want to prevent cgminer from -scanning without specifying a device, you can use "-S noauto". Note that -presently, autodetection only works on Linux, and might only detect one -device depending on the version of udev being used. - -On linux is usually of the format /dev/ttyUSBn -On windows is usually of the format \\.\COMn -(where n = the correct device number for the Icarus device) - -The official supplied binaries are compiled with support for all FPGAs. -To force the code to only attempt detection with a specific driver, -prepend the argument with the driver name followed by a colon. -For example, "icarus:/dev/ttyUSB0" or using the short name: "ica:/dev/ttyUSB0" -This option not longer matters since Icarus is the only serial-USB -device that uses it For other FPGA details see the FPGA-README @@ -657,27 +639,6 @@ A: Cgminer currently supports 2 ASICs: Avalon and BitForce SC devices. They are Application Specify Integrated Circuit devices and provide the highest performance per unit power due to being dedicated to only one purpose. -Q: How do I get my Icarus/Lancelot/Cairnsmore device to auto-recognise? -A: On linux, if the /dev/ttyUSB* devices don't automatically appear, the only -thing that needs to be done is to load the driver for them: -Icarus: sudo modprobe pl2303 vendor=0x067b product=0x230 -Lancelot: sudo modprobe ftdi_sio vendor=0x0403 product=0x6001 -Cairnsmore: sudo modprobe ftdi_sio product=0x8350 vendor=0x0403 -On windows you must install the pl2303 or ftdi driver required for the device -pl2303: http://prolificusa.com/pl-2303hx-drivers/ -ftdi: http://www.ftdichip.com/Drivers/VCP.htm - -Q: On linux I can see the /dev/ttyUSB* devices for my Icarus FPGAs, but -cgminer can't mine on them -A: Make sure you have the required priviledges to access the /dev/ttyUSB* devices: - sudo ls -las /dev/ttyUSB* -will give output like: - 0 crw-rw---- 1 root dialout 188, 0 2012-09-11 13:49 /dev/ttyUSB0 -This means your account must have the group 'dialout' or root priviledges -To permanently give your account the 'dialout' group: - sudo usermod -G dialout -a `whoami` -Then logout and back in again - Q: Can I mine scrypt with FPGAs or ASICs? A: No. diff --git a/adl.c b/adl.c index 1c650e5c..41b0942d 100644 --- a/adl.c +++ b/adl.c @@ -493,9 +493,16 @@ void init_adl(int nDevs) if (!gpus[gpu].cutofftemp) gpus[gpu].cutofftemp = opt_cutofftemp; if (opt_autofan) { - ga->autofan = true; /* Set a safe starting default if we're automanaging fan speeds */ - set_fanspeed(gpu, 50); + int nominal = 50; + + ga->autofan = true; + /* Clamp fanspeed values to range provided */ + if (nominal > gpus[gpu].gpu_fan) + nominal = gpus[gpu].gpu_fan; + if (nominal < gpus[gpu].min_fan) + nominal = gpus[gpu].min_fan; + set_fanspeed(gpu, nominal); } if (opt_autoengine) { ga->autoengine = true; diff --git a/cgminer.c b/cgminer.c index eaccf508..bfe1db33 100644 --- a/cgminer.c +++ b/cgminer.c @@ -28,6 +28,10 @@ #include #include +#ifdef USE_USBUTILS +#include +#endif + #include #include @@ -60,9 +64,6 @@ #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_MODMINER) # define USE_FPGA -#if defined(USE_ICARUS) || defined(USE_AVALON) -# define USE_FPGA_SERIAL -#endif #elif defined(USE_ZTEX) # define USE_FPGA #endif @@ -147,6 +148,7 @@ char *opt_avalon_options = NULL; char *opt_usb_select = NULL; int opt_usbdump = -1; bool opt_usb_list_all; +sem_t usb_resource_sem; #endif char *opt_kernel_path; @@ -169,6 +171,7 @@ static int input_thr_id; int gpur_thr_id; static int api_thr_id; #ifdef USE_USBUTILS +static int usbres_thr_id; static int hotplug_thr_id; #endif static int total_control_threads; @@ -180,6 +183,7 @@ int hotplug_time = 5; #ifdef USE_USBUTILS pthread_mutex_t cgusb_lock; +pthread_mutex_t cgusbres_lock; #endif pthread_mutex_t hash_lock; @@ -1089,7 +1093,7 @@ static struct opt_table opt_config_table[] = { #ifdef USE_FPGA_SERIAL OPT_WITH_ARG("--scan-serial|-S", add_serial, NULL, NULL, - "Serial port to probe for Icarus FPGA Mining device"), + "Serial port to probe for Serial FPGA Mining device"), #endif OPT_WITH_ARG("--scan-time|-s", set_int_0_to_9999, opt_show_intval, &opt_scantime, @@ -2221,6 +2225,7 @@ void logwin_update(void) if (curses_active_locked()) { touchwin(logwin); wrefresh(logwin); + unlock_curses(); } } #endif @@ -2809,6 +2814,22 @@ static void __kill_work(void) thr = &control_thr[watchdog_thr_id]; thr_info_cancel(thr); + applog(LOG_DEBUG, "Shutting down mining threads"); + for (i = 0; i < mining_threads; i++) { + struct cgpu_info *cgpu; + + thr = get_thread(i); + if (!thr) + continue; + cgpu = thr->cgpu; + if (!cgpu) + continue; + + cgpu->shutdown = true; + } + + sleep(1); + applog(LOG_DEBUG, "Killing off mining threads"); /* Kill the mining threads*/ for (i = 0; i < mining_threads; i++) { @@ -2818,8 +2839,13 @@ static void __kill_work(void) if (thr && PTH(thr) != 0L) pth = &thr->pth; thr_info_cancel(thr); - if (pth) +#ifndef WIN32 + if (pth && *pth) + pthread_join(*pth, NULL); +#else + if (pth && pth->p) pthread_join(*pth, NULL); +#endif } applog(LOG_DEBUG, "Killing off stage thread"); @@ -2837,6 +2863,10 @@ static void __kill_work(void) if (!opt_scrypt) { applog(LOG_DEBUG, "Releasing all USB devices"); usb_cleanup(); + + applog(LOG_DEBUG, "Killing off usbres thread"); + thr = &control_thr[usbres_thr_id]; + thr_info_cancel(thr); } #endif @@ -5505,7 +5535,7 @@ static struct work *get_work(struct thr_info *thr, const int thr_id) return work; } -void submit_work_async(struct work *work_in, struct timeval *tv_work_found) +static void submit_work_async(struct work *work_in, struct timeval *tv_work_found) { struct work *work = copy_work(work_in); struct pool *pool = work->pool; @@ -5559,13 +5589,17 @@ void inc_hw_errors(struct thr_info *thr) thr->cgpu->drv->hw_error(thr); } -void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) +/* Returns true if nonce for work was a valid share */ +bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) { uint32_t *work_nonce = (uint32_t *)(work->data + 64 + 12); struct timeval tv_work_found; unsigned char hash2[32]; uint32_t *hash2_32 = (uint32_t *)hash2; uint32_t diff1targ; + bool ret = true; + + thread_reportout(thr); cgtime(&tv_work_found); *work_nonce = htole32(nonce); @@ -5586,7 +5620,8 @@ void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) thr->cgpu->drv->name, thr->cgpu->device_id); inc_hw_errors(thr); - return; + ret = false; + goto out; } mutex_lock(&stats_lock); @@ -5595,10 +5630,14 @@ void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) if (!fulltest(hash2, work->target)) { applog(LOG_INFO, "Share below target"); - return; + goto out; } submit_work_async(work, &tv_work_found); +out: + thread_reportin(thr); + + return ret; } static inline bool abandon_work(struct work *work, struct timeval *wdiff, uint64_t hashes) @@ -5649,7 +5688,7 @@ static void hash_sole_work(struct thr_info *mythr) sdiff.tv_sec = sdiff.tv_usec = 0; cgtime(&tv_lastupdate); - while (42) { + while (likely(!cgpu->shutdown)) { struct work *work = get_work(mythr, thr_id); int64_t hashes; @@ -5788,6 +5827,7 @@ static void hash_sole_work(struct thr_info *mythr) } while (!abandon_work(work, &wdiff, cgpu->max_hashes)); free_work(work); } + cgpu->deven = DEV_DISABLED; } /* Create a hashtable of work items for devices with a queue. The device @@ -5925,7 +5965,7 @@ void hash_queued_work(struct thr_info *mythr) const int thr_id = mythr->id; int64_t hashes_done = 0; - while (42) { + while (likely(!cgpu->shutdown)) { struct timeval diff; int64_t hashes; @@ -5960,6 +6000,7 @@ void hash_queued_work(struct thr_info *mythr) drv->flush_work(cgpu); } } + cgpu->deven = DEV_DISABLED; } void *miner_thread(void *userdata) @@ -7111,6 +7152,10 @@ static void *hotplug_thread(void __maybe_unused *userdata) new_devices = 0; new_threads = 0; +#ifdef USE_ICARUS + icarus_drv.drv_detect(); +#endif + #ifdef USE_BFLSC bflsc_drv.drv_detect(); #endif @@ -7123,6 +7168,10 @@ static void *hotplug_thread(void __maybe_unused *userdata) modminer_drv.drv_detect(); #endif +#ifdef USE_AVALON + avalon_drv.drv_detect(); +#endif + if (new_devices) hotplug_process(); @@ -7175,6 +7224,7 @@ int main(int argc, char *argv[]) } #ifdef USE_USBUTILS mutex_init(&cgusb_lock); + mutex_init(&cgusbres_lock); #endif #endif @@ -7301,8 +7351,27 @@ int main(int argc, char *argv[]) /* Use a shorter scantime for scrypt */ if (opt_scantime < 0) opt_scantime = opt_scrypt ? 30 : 60; + + total_control_threads = 9; + control_thr = calloc(total_control_threads, sizeof(*thr)); + if (!control_thr) + quit(1, "Failed to calloc control_thr"); + + gwsched_thr_id = 0; + #ifdef USE_USBUTILS usb_initialise(); + + // before device detection + if (!opt_scrypt) { + if (sem_init(&usb_resource_sem, 0, 0)) + quit(1, "Failed to sem_init usb_resource_sem"); + usbres_thr_id = 1; + thr = &control_thr[usbres_thr_id]; + if (thr_info_create(thr, NULL, usb_resource_thread, thr)) + quit(1, "usb resource thread create failed"); + pthread_detach(thr->pth); + } #endif #ifdef HAVE_OPENCL @@ -7316,11 +7385,6 @@ int main(int argc, char *argv[]) icarus_drv.drv_detect(); #endif -#ifdef USE_AVALON - if (!opt_scrypt) - avalon_drv.drv_detect(); -#endif - #ifdef USE_BFLSC if (!opt_scrypt) bflsc_drv.drv_detect(); @@ -7341,6 +7405,13 @@ int main(int argc, char *argv[]) ztex_drv.drv_detect(); #endif + /* Detect avalon last since it will try to claim the device regardless + * as detection is unreliable. */ +#ifdef USE_AVALON + if (!opt_scrypt) + avalon_drv.drv_detect(); +#endif + if (devices_enabled == -1) { applog(LOG_ERR, "Devices detected:"); for (i = 0; i < total_devices; ++i) { @@ -7442,13 +7513,7 @@ int main(int argc, char *argv[]) quit(1, "Failed to calloc mining_thr[%d]", i); } - total_control_threads = 8; - control_thr = calloc(total_control_threads, sizeof(*thr)); - if (!control_thr) - quit(1, "Failed to calloc control_thr"); - - gwsched_thr_id = 0; - stage_thr_id = 1; + stage_thr_id = 2; thr = &control_thr[stage_thr_id]; thr->q = tq_new(); if (!thr->q) @@ -7569,14 +7634,14 @@ begin_bench: cgtime(&total_tv_start); cgtime(&total_tv_end); - watchpool_thr_id = 2; + watchpool_thr_id = 3; thr = &control_thr[watchpool_thr_id]; /* start watchpool thread */ if (thr_info_create(thr, NULL, watchpool_thread, NULL)) quit(1, "watchpool thread create failed"); pthread_detach(thr->pth); - watchdog_thr_id = 3; + watchdog_thr_id = 4; thr = &control_thr[watchdog_thr_id]; /* start watchdog thread */ if (thr_info_create(thr, NULL, watchdog_thread, NULL)) @@ -7585,7 +7650,7 @@ begin_bench: #ifdef HAVE_OPENCL /* Create reinit gpu thread */ - gpur_thr_id = 4; + gpur_thr_id = 5; thr = &control_thr[gpur_thr_id]; thr->q = tq_new(); if (!thr->q) @@ -7595,14 +7660,14 @@ begin_bench: #endif /* Create API socket thread */ - api_thr_id = 5; + api_thr_id = 6; thr = &control_thr[api_thr_id]; if (thr_info_create(thr, NULL, api_thread, thr)) quit(1, "API thread create failed"); #ifdef USE_USBUTILS if (!opt_scrypt) { - hotplug_thr_id = 6; + hotplug_thr_id = 7; thr = &control_thr[hotplug_thr_id]; if (thr_info_create(thr, NULL, hotplug_thread, thr)) quit(1, "hotplug thread create failed"); @@ -7614,7 +7679,7 @@ begin_bench: /* Create curses input thread for keyboard input. Create this last so * that we know all threads are created since this can call kill_work * to try and shut down all previous threads. */ - input_thr_id = 7; + input_thr_id = 8; thr = &control_thr[input_thr_id]; if (thr_info_create(thr, NULL, input_thread, thr)) quit(1, "input thread create failed"); @@ -7622,8 +7687,8 @@ begin_bench: #endif /* Just to be sure */ - if (total_control_threads != 8) - quit(1, "incorrect total_control_threads (%d) should be 8", total_control_threads); + if (total_control_threads != 9) + quit(1, "incorrect total_control_threads (%d) should be 9", total_control_threads); /* Once everything is set up, main() becomes the getwork scheduler */ while (42) { diff --git a/configure.ac b/configure.ac index 804cd218..b4f61471 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_maj], [3]) -m4_define([v_min], [1]) -m4_define([v_mic], [1]) +m4_define([v_min], [2]) +m4_define([v_mic], [0]) ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([lt_rev], m4_eval(v_maj + v_min)) @@ -303,14 +303,14 @@ else ]) fi -AM_CONDITIONAL([NEED_FPGAUTILS], [test x$avalon$icarus$bitforce$modminer$ztex != xnonononono]) -AM_CONDITIONAL([NEED_USBUTILS_C], [test x$bitforce$modminer$bflsc != xnonono]) +AM_CONDITIONAL([NEED_FPGAUTILS], [test x$modminer$ztex != xnono]) +AM_CONDITIONAL([NEED_USBUTILS_C], [test x$avalon$bitforce$modminer$bflsc$icarus != xnonononono]) AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes]) AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue]) AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue]) AM_CONDITIONAL([HAVE_x86_64], [test x$have_x86_64 = xtrue]) -if test "x$bitforce$modminer$bflsc" != xnonono; then +if test "x$avalon$bitforce$modminer$bflsc$icarus" != xnonononono; then AC_DEFINE([USE_USBUTILS], [1], [Defined to 1 if usbutils support required]) fi @@ -321,26 +321,6 @@ else JANSSON_LIBS=-ljansson fi -if test "x$icarus" != xno; then - AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])], - [libudev=$withval], - [libudev=auto] - ) - if test "x$libudev" != "xno"; then - AC_CHECK_HEADER([libudev.h],[ - libudev=yes - UDEV_LIBS=-ludev - AC_DEFINE([HAVE_LIBUDEV], [1], [Defined to 1 if libudev is wanted]) - ], [ - if test "x$libudev" = "xyes"; then - AC_MSG_ERROR([libudev not found]) - fi - libudev=no - ]) - fi -fi -AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno]) - PKG_PROG_PKG_CONFIG() if test "x$have_cgminer_sdk" = "xtrue"; then @@ -353,7 +333,7 @@ if test "x$have_cgminer_sdk" = "xtrue"; then PKG_CONFIG_PATH="$CGMINER_SDK/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" fi -if test "x$ztex$modminer$bitforce$bflsc" != xnononono; then +if test "x$avalon$ztex$modminer$bitforce$bflsc$icarus" != xnononononono; then case $target in *-*-freebsd*) LIBUSB_LIBS="-lusb" @@ -441,7 +421,6 @@ AC_SUBST(PDCURSES_LIBS) AC_SUBST(WS2_LIBS) AC_SUBST(MM_LIBS) AC_SUBST(MATH_LIBS) -AC_SUBST(UDEV_LIBS) AC_SUBST(ADL_CPPFLAGS) AC_CONFIG_FILES([ @@ -538,16 +517,12 @@ else echo " Ztex.FPGAs...........: Disabled" fi -if test "x$icarus" != xno; then - echo " libudev.detection....: $libudev" -fi - echo echo "Compilation............: make (or gmake)" echo " CPPFLAGS.............: $CPPFLAGS" echo " CFLAGS...............: $CFLAGS $LIBUSB_CFLAGS" echo " LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS" -echo " LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $OPENCL_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $LIBUSB_LIBS" +echo " LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $OPENCL_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $LIBUSB_LIBS" echo echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" echo " prefix...............: $prefix" diff --git a/driver-avalon.c b/driver-avalon.c index 15ded4e2..4136e49e 100644 --- a/driver-avalon.c +++ b/driver-avalon.c @@ -35,13 +35,12 @@ #include "elist.h" #include "miner.h" -#include "fpgautils.h" +#include "usbutils.h" #include "driver-avalon.h" #include "hexdump.c" #include "util.h" static int option_offset = -1; -struct avalon_info **avalon_infos; struct device_drv avalon_drv; static int avalon_init_task(struct avalon_task *at, @@ -121,19 +120,36 @@ static inline void avalon_create_task(struct avalon_task *at, memcpy(at->data, work->data + 64, 12); } -static int avalon_send_task(int fd, const struct avalon_task *at, - struct cgpu_info *avalon) +static int avalon_write(struct cgpu_info *avalon, char *buf, ssize_t len, int ep) +{ + int err, amount; + + err = usb_write(avalon, buf, len, &amount, ep); + applog(LOG_DEBUG, "%s%i: usb_write got err %d", avalon->drv->name, + avalon->device_id, err); + + if (unlikely(err != 0)) { + applog(LOG_WARNING, "usb_write error on avalon_write"); + return AVA_SEND_ERROR; + } + if (amount != len) { + applog(LOG_WARNING, "usb_write length mismatch on avalon_write"); + return AVA_SEND_ERROR; + } + + return AVA_SEND_OK; +} + +static int avalon_send_task(const struct avalon_task *at, struct cgpu_info *avalon) { - size_t ret; - int full; struct timespec p; uint8_t buf[AVALON_WRITE_SIZE + 4 * AVALON_DEFAULT_ASIC_NUM]; size_t nr_len; struct avalon_info *info; uint64_t delay = 32000000; /* Default 32ms for B19200 */ uint32_t nonce_range; - int i; + int ret, i, ep = C_AVALON_TASK; if (at->nonce_elf) nr_len = AVALON_WRITE_SIZE + 4 * at->asic_num; @@ -173,176 +189,84 @@ static int avalon_send_task(int fd, const struct avalon_task *at, buf[4] = tt; #endif if (likely(avalon)) { - info = avalon_infos[avalon->device_id]; + info = avalon->device_data; delay = nr_len * 10 * 1000000000ULL; delay = delay / info->baud; } - if (at->reset) + if (at->reset) { + ep = C_AVALON_RESET; nr_len = 1; + } if (opt_debug) { applog(LOG_DEBUG, "Avalon: Sent(%u):", (unsigned int)nr_len); - hexdump((uint8_t *)buf, nr_len); + hexdump(buf, nr_len); } - ret = write(fd, buf, nr_len); - if (unlikely(ret != nr_len)) - return AVA_SEND_ERROR; + ret = avalon_write(avalon, (char *)buf, nr_len, ep); p.tv_sec = 0; p.tv_nsec = (long)delay + 4000000; nanosleep(&p, NULL); applog(LOG_DEBUG, "Avalon: Sent: Buffer delay: %ld", p.tv_nsec); - full = avalon_buffer_full(fd); - applog(LOG_DEBUG, "Avalon: Sent: Buffer full: %s", - ((full == AVA_BUFFER_FULL) ? "Yes" : "No")); - - if (unlikely(full == AVA_BUFFER_FULL)) - return AVA_SEND_BUFFER_FULL; - - return AVA_SEND_BUFFER_EMPTY; + return ret; } -static inline int avalon_gets(int fd, uint8_t *buf, struct thr_info *thr, - struct timeval *tv_finish) +static bool avalon_decode_nonce(struct thr_info *thr, struct cgpu_info *avalon, + struct avalon_info *info, struct avalon_result *ar, + struct work *work) { - int read_amount = AVALON_READ_SIZE; - bool first = true; - ssize_t ret = 0; - - while (true) { - struct timeval timeout; - fd_set rd; - - if (unlikely(thr->work_restart)) { - applog(LOG_DEBUG, "Avalon: Work restart"); - return AVA_GETS_RESTART; - } - - timeout.tv_sec = 0; - timeout.tv_usec = 100000; - - FD_ZERO(&rd); - FD_SET((SOCKETTYPE)fd, &rd); - ret = select(fd + 1, &rd, NULL, NULL, &timeout); - if (unlikely(ret < 0)) { - applog(LOG_ERR, "Avalon: Error %d on select in avalon_gets", errno); - return AVA_GETS_ERROR; - } - if (ret) { - ret = read(fd, buf, read_amount); - if (unlikely(ret < 0)) { - applog(LOG_ERR, "Avalon: Error %d on read in avalon_gets", errno); - return AVA_GETS_ERROR; - } - if (likely(first)) { - cgtime(tv_finish); - first = false; - } - if (likely(ret >= read_amount)) - return AVA_GETS_OK; - buf += ret; - read_amount -= ret; - continue; - } - - if (unlikely(thr->work_restart)) { - applog(LOG_DEBUG, "Avalon: Work restart"); - return AVA_GETS_RESTART; - } + uint32_t nonce; - return AVA_GETS_TIMEOUT; - } + info = avalon->device_data; + info->matching_work[work->subid]++; + nonce = htole32(ar->nonce); + applog(LOG_DEBUG, "Avalon: nonce = %0x08x", nonce); + return submit_nonce(thr, work, nonce); } -static int avalon_get_result(int fd, struct avalon_result *ar, - struct thr_info *thr, struct timeval *tv_finish) +/* Wait until the ftdi chip returns a CTS saying we can send more data. The + * status is updated every 40ms. */ +static void wait_avalon_ready(struct cgpu_info *avalon) { - uint8_t result[AVALON_READ_SIZE]; - int ret; - - memset(result, 0, AVALON_READ_SIZE); - ret = avalon_gets(fd, result, thr, tv_finish); - - if (ret == AVA_GETS_OK) { - if (opt_debug) { - applog(LOG_DEBUG, "Avalon: get:"); - hexdump((uint8_t *)result, AVALON_READ_SIZE); - } - memcpy((uint8_t *)ar, result, AVALON_READ_SIZE); + while (avalon_buffer_full(avalon)) { + nmsleep(40); } - - return ret; } -static bool avalon_decode_nonce(struct thr_info *thr, struct avalon_result *ar, - uint32_t *nonce) +static int avalon_read(struct cgpu_info *avalon, unsigned char *buf, + size_t bufsize, int timeout, int ep) { - struct cgpu_info *avalon; - struct avalon_info *info; - struct work *work; - - avalon = thr->cgpu; - if (unlikely(!avalon->works)) - return false; - - work = find_queued_work_bymidstate(avalon, (char *)ar->midstate, 32, - (char *)ar->data, 64, 12); - if (!work) - return false; - - info = avalon_infos[avalon->device_id]; - info->matching_work[work->subid]++; - *nonce = htole32(ar->nonce); - submit_nonce(thr, work, *nonce); - - return true; -} - -static void avalon_get_reset(int fd, struct avalon_result *ar) -{ - int read_amount = AVALON_READ_SIZE; - uint8_t result[AVALON_READ_SIZE]; - struct timeval timeout = {1, 0}; - ssize_t ret = 0, offset = 0; - fd_set rd; - - memset(result, 0, AVALON_READ_SIZE); - memset(ar, 0, AVALON_READ_SIZE); - FD_ZERO(&rd); - FD_SET((SOCKETTYPE)fd, &rd); - ret = select(fd + 1, &rd, NULL, NULL, &timeout); - if (unlikely(ret < 0)) { - applog(LOG_WARNING, "Avalon: Error %d on select in avalon_get_reset", errno); - return; - } - if (!ret) { - applog(LOG_WARNING, "Avalon: Timeout on select in avalon_get_reset"); - return; - } - do { - ret = read(fd, result + offset, read_amount); - if (unlikely(ret < 0)) { - applog(LOG_WARNING, "Avalon: Error %d on read in avalon_get_reset", errno); - return; - } - read_amount -= ret; - offset += ret; - } while (read_amount > 0); - if (opt_debug) { - applog(LOG_DEBUG, "Avalon: get:"); - hexdump((uint8_t *)result, AVALON_READ_SIZE); + size_t total = 0, readsize = bufsize + 2; + char readbuf[AVALON_READBUF_SIZE]; + int err, amount, ofs = 2, cp; + + err = usb_read_once_timeout(avalon, readbuf, readsize, &amount, timeout, ep); + applog(LOG_DEBUG, "%s%i: Get avalon read got err %d", + avalon->drv->name, avalon->device_id, err); + + /* The first 2 of every 64 bytes are status on FTDIRL */ + while (amount > 2) { + cp = amount - 2; + if (cp > 62) + cp = 62; + memcpy(&buf[total], &readbuf[ofs], cp); + total += cp; + amount -= cp + 2; + ofs += 64; } - memcpy((uint8_t *)ar, result, AVALON_READ_SIZE); + return total; } -static int avalon_reset(int fd, struct avalon_result *ar) +static int avalon_reset(struct cgpu_info *avalon, bool initial) { + struct avalon_result ar; + int ret, i, spare; struct avalon_task at; - uint8_t *buf; - int ret, i = 0; + uint8_t *buf, *tmp; struct timespec p; + /* Send reset, then check for result */ avalon_init_task(&at, 1, 0, AVALON_DEFAULT_FAN_MAX_PWM, AVALON_DEFAULT_TIMEOUT, @@ -350,17 +274,41 @@ static int avalon_reset(int fd, struct avalon_result *ar) AVALON_DEFAULT_MINER_NUM, 0, 0, AVALON_DEFAULT_FREQUENCY); - ret = avalon_send_task(fd, &at, NULL); - if (ret == AVA_SEND_ERROR) - return 1; - avalon_get_reset(fd, ar); + wait_avalon_ready(avalon); + ret = avalon_send_task(&at, avalon); + if (unlikely(ret == AVA_SEND_ERROR)) + return -1; + + if (!initial) { + applog(LOG_ERR, "AVA%d reset sequence sent", avalon->device_id); + return 0; + } + + ret = avalon_read(avalon, (unsigned char *)&ar, AVALON_READ_SIZE, + AVALON_RESET_TIMEOUT, C_GET_AVALON_RESET); + + /* What do these sleeps do?? */ + p.tv_sec = 0; + p.tv_nsec = AVALON_RESET_PITCH; + nanosleep(&p, NULL); + + /* Look for the first occurrence of 0xAA, the reset response should be: + * AA 55 AA 55 00 00 00 00 00 00 */ + spare = ret - 10; + buf = tmp = (uint8_t *)&ar; + if (opt_debug) { + applog(LOG_DEBUG, "AVA%d reset: get:", avalon->device_id); + hexdump(tmp, AVALON_READ_SIZE); + } + + for (i = 0; i <= spare; i++) { + buf = &tmp[i]; + if (buf[0] == 0xAA) + break; + } + i = 0; - buf = (uint8_t *)ar; - /* Sometimes there is one extra 0 byte for some reason in the buffer, - * so work around it. */ - if (buf[0] == 0) - buf = (uint8_t *)(ar + 1); if (buf[0] == 0xAA && buf[1] == 0x55 && buf[2] == 0xAA && buf[3] == 0x55) { for (i = 4; i < 11; i++) @@ -368,53 +316,19 @@ static int avalon_reset(int fd, struct avalon_result *ar) break; } - p.tv_sec = 0; - p.tv_nsec = AVALON_RESET_PITCH; - nanosleep(&p, NULL); - if (i != 11) { - applog(LOG_ERR, "Avalon: Reset failed! not an Avalon?" - " (%d: %02x %02x %02x %02x)", + applog(LOG_ERR, "AVA%d: Reset failed! not an Avalon?" + " (%d: %02x %02x %02x %02x)", avalon->device_id, i, buf[0], buf[1], buf[2], buf[3]); /* FIXME: return 1; */ } else - applog(LOG_WARNING, "Avalon: Reset succeeded"); - return 0; -} + applog(LOG_WARNING, "AVA%d: Reset succeeded", + avalon->device_id); -static void avalon_idle(struct cgpu_info *avalon) -{ - int i, ret; - struct avalon_task at; - - int fd = avalon->device_fd; - struct avalon_info *info = avalon_infos[avalon->device_id]; - int avalon_get_work_count = info->miner_count; - - i = 0; - while (true) { - avalon_init_task(&at, 0, 0, info->fan_pwm, - info->timeout, info->asic_count, - info->miner_count, 1, 1, info->frequency); - ret = avalon_send_task(fd, &at, avalon); - if (unlikely(ret == AVA_SEND_ERROR || - (ret == AVA_SEND_BUFFER_EMPTY && - (i + 1 == avalon_get_work_count * 2)))) { - applog(LOG_ERR, "AVA%i: Comms error", avalon->device_id); - return; - } - if (i + 1 == avalon_get_work_count * 2) - break; - - if (ret == AVA_SEND_BUFFER_FULL) - break; - - i++; - } - applog(LOG_ERR, "Avalon: Goto idle mode"); + return 0; } -static void get_options(int this_option_offset, int *baud, int *miner_count, +static bool get_options(int this_option_offset, int *baud, int *miner_count, int *asic_count, int *timeout, int *frequency) { char err_buf[BUFSIZ+1]; @@ -446,14 +360,8 @@ static void get_options(int this_option_offset, int *baud, int *miner_count, buf[max] = '\0'; } - *baud = AVALON_IO_SPEED; - *miner_count = AVALON_DEFAULT_MINER_NUM - 8; - *asic_count = AVALON_DEFAULT_ASIC_NUM; - *timeout = AVALON_DEFAULT_TIMEOUT; - *frequency = AVALON_DEFAULT_FREQUENCY; - if (!(*buf)) - return; + return false; colon = strchr(buf, ':'); if (colon) @@ -548,65 +456,162 @@ static void get_options(int this_option_offset, int *baud, int *miner_count, } } } + return true; } -static bool avalon_detect_one(const char *devpath) +static void avalon_idle(struct cgpu_info *avalon, struct avalon_info *info) { - struct avalon_info *info; - struct avalon_result ar; - int fd, ret; - int baud, miner_count, asic_count, timeout, frequency = 0; - struct cgpu_info *avalon; - - int this_option_offset = ++option_offset; - get_options(this_option_offset, &baud, &miner_count, &asic_count, - &timeout, &frequency); + int i; - applog(LOG_DEBUG, "Avalon Detect: Attempting to open %s " - "(baud=%d miner_count=%d asic_count=%d timeout=%d frequency=%d)", - devpath, baud, miner_count, asic_count, timeout, frequency); + info->idle = true; + wait_avalon_ready(avalon); + /* Send idle to all miners */ + for (i = 0; i < info->miner_count; i++) { + struct avalon_task at; - fd = avalon_open2(devpath, baud, true); - if (unlikely(fd == -1)) { - applog(LOG_ERR, "Avalon Detect: Failed to open %s", devpath); - return false; + if (unlikely(avalon_buffer_full(avalon))) + break; + avalon_init_task(&at, 0, 0, info->fan_pwm, info->timeout, + info->asic_count, info->miner_count, 1, 1, + info->frequency); + avalon_send_task(&at, avalon); } + applog(LOG_WARNING, "AVA%i: Idling %d miners", avalon->device_id, i); + wait_avalon_ready(avalon); +} + +static void avalon_initialise(struct cgpu_info *avalon) +{ + int err, interface; + + if (avalon->usbinfo.nodev) + return; + + interface = avalon->usbdev->found->interface; + // Reset + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, + FTDI_VALUE_RESET, interface, C_RESET); + + applog(LOG_DEBUG, "%s%i: reset got err %d", + avalon->drv->name, avalon->device_id, err); + + if (avalon->usbinfo.nodev) + return; + + // Set data + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, + FTDI_VALUE_DATA_AVA, interface, C_SETDATA); + + applog(LOG_DEBUG, "%s%i: data got err %d", + avalon->drv->name, avalon->device_id, err); + + if (avalon->usbinfo.nodev) + return; + + // Set the baud + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_AVA, + (FTDI_INDEX_BAUD_AVA & 0xff00) | interface, + C_SETBAUD); + + applog(LOG_DEBUG, "%s%i: setbaud got err %d", + avalon->drv->name, avalon->device_id, err); + + if (avalon->usbinfo.nodev) + return; + + // Set Modem Control + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, interface, C_SETMODEM); + + applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", + avalon->drv->name, avalon->device_id, err); + + if (avalon->usbinfo.nodev) + return; + + // Set Flow Control + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, interface, C_SETFLOW); + + applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", + avalon->drv->name, avalon->device_id, err); + + if (avalon->usbinfo.nodev) + return; + + /* Avalon repeats the following */ + // Set Modem Control + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, interface, C_SETMODEM); + + applog(LOG_DEBUG, "%s%i: setmodemctrl 2 got err %d", + avalon->drv->name, avalon->device_id, err); + + if (avalon->usbinfo.nodev) + return; + + // Set Flow Control + err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, interface, C_SETFLOW); + + applog(LOG_DEBUG, "%s%i: setflowctrl 2 got err %d", + avalon->drv->name, avalon->device_id, err); +} + +static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found) +{ + int baud, miner_count, asic_count, timeout, frequency = 0; + int this_option_offset = ++option_offset; + struct avalon_info *info; + struct cgpu_info *avalon; + char devpath[20]; + bool configured; + int ret; - /* We have a real Avalon! */ avalon = calloc(1, sizeof(struct cgpu_info)); + if (unlikely(!avalon)) + quit(1, "Failed to calloc avalon in avalon_detect_one");; avalon->drv = &avalon_drv; - avalon->device_path = strdup(devpath); - avalon->device_fd = fd; avalon->threads = AVALON_MINER_THREADS; - add_cgpu(avalon); - ret = avalon_reset(fd, &ar); - if (ret) { - ; /* FIXME: I think IT IS avalon and wait on reset; - * avalon_close(fd); - * return false; */ - } - - avalon_infos = realloc(avalon_infos, - sizeof(struct avalon_info *) * - (total_devices + 1)); + configured = get_options(this_option_offset, &baud, &miner_count, + &asic_count, &timeout, &frequency); + + if (!usb_init(avalon, dev, found)) + return false; - applog(LOG_INFO, "Avalon Detect: Found at %s, mark as %d", - devpath, avalon->device_id); + /* Even though this is an FTDI type chip, we want to do the parsing + * all ourselves so set it to std usb type */ + avalon->usbdev->usb_type = USB_TYPE_STD; - avalon_infos[avalon->device_id] = (struct avalon_info *) - malloc(sizeof(struct avalon_info)); - if (unlikely(!(avalon_infos[avalon->device_id]))) - quit(1, "Failed to malloc avalon_infos"); + /* We have a real Avalon! */ + sprintf(devpath, "%d:%d", + (int)(avalon->usbinfo.bus_number), + (int)(avalon->usbinfo.device_address)); - info = avalon_infos[avalon->device_id]; + avalon_initialise(avalon); - memset(info, 0, sizeof(struct avalon_info)); + avalon->device_path = strdup(devpath); + add_cgpu(avalon); - info->baud = baud; - info->miner_count = miner_count; - info->asic_count = asic_count; - info->timeout = timeout; + avalon->device_data = calloc(sizeof(struct avalon_info), 1); + if (unlikely(!(avalon->device_data))) + quit(1, "Failed to malloc avalon_info data"); + info = avalon->device_data; + + if (configured) { + info->baud = baud; + info->miner_count = miner_count; + info->asic_count = asic_count; + info->timeout = timeout; + info->frequency = frequency; + } else { + info->baud = AVALON_IO_SPEED; + info->miner_count = AVALON_DEFAULT_MINER_NUM; + info->asic_count = AVALON_DEFAULT_ASIC_NUM; + info->timeout = AVALON_DEFAULT_TIMEOUT; + info->frequency = AVALON_DEFAULT_FREQUENCY; + } info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM; info->temp_max = 0; @@ -618,54 +623,260 @@ static bool avalon_detect_one(const char *devpath) info->temp_history_index = 0; info->temp_sum = 0; info->temp_old = 0; - info->frequency = frequency; - /* Set asic to idle mode after detect */ - avalon_idle(avalon); - avalon->device_fd = -1; + ret = avalon_reset(avalon, true); + if (ret && !configured) { + usb_uninit(avalon); + return false; + } + + avalon_idle(avalon, info); + + applog(LOG_DEBUG, "Avalon Detected: %s " + "(miner_count=%d asic_count=%d timeout=%d frequency=%d)", + devpath, info->miner_count, info->asic_count, info->timeout, + info->frequency); - avalon_close(fd); return true; } -static inline void avalon_detect() +static void avalon_detect(void) { - serial_detect(&avalon_drv, avalon_detect_one); + usb_detect(&avalon_drv, avalon_detect_one); } -static void __avalon_init(struct cgpu_info *avalon) +static void avalon_init(struct cgpu_info *avalon) { applog(LOG_INFO, "Avalon: Opened on %s", avalon->device_path); } -static void avalon_init(struct cgpu_info *avalon) +static struct work *avalon_valid_result(struct cgpu_info *avalon, struct avalon_result *ar) { - struct avalon_result ar; - int fd, ret; - - avalon->device_fd = -1; - fd = avalon_open(avalon->device_path, - avalon_infos[avalon->device_id]->baud); - if (unlikely(fd == -1)) { - applog(LOG_ERR, "Avalon: Failed to open on %s", - avalon->device_path); + return find_queued_work_bymidstate(avalon, (char *)ar->midstate, 32, + (char *)ar->data, 64, 12); +} + +static void avalon_update_temps(struct cgpu_info *avalon, struct avalon_info *info, + struct avalon_result *ar); + +static void avalon_inc_nvw(struct avalon_info *info, struct thr_info *thr) +{ + if (unlikely(info->idle)) return; + + applog(LOG_WARNING, "%s%d: No valid work - HW error", + thr->cgpu->drv->name, thr->cgpu->device_id); + + inc_hw_errors(thr); + info->no_matching_work++; +} + +static void avalon_parse_results(struct cgpu_info *avalon, struct avalon_info *info, + struct thr_info *thr, char *buf, int *offset) +{ + int i, spare = *offset - AVALON_READ_SIZE; + bool found = false; + + for (i = 0; i <= spare; i++) { + struct avalon_result *ar; + struct work *work; + + ar = (struct avalon_result *)&buf[i]; + work = avalon_valid_result(avalon, ar); + if (work) { + bool gettemp = false; + + found = true; + + if (avalon_decode_nonce(thr, avalon, info, ar, work)) { + mutex_lock(&info->lock); + if (!info->nonces++) + gettemp = true; + mutex_unlock(&info->lock); + } + + if (gettemp) + avalon_update_temps(avalon, info, ar); + break; + } } - ret = avalon_reset(fd, &ar); - if (ret) { - avalon_close(fd); - return; + if (!found) { + spare = *offset - AVALON_READ_SIZE; + /* We are buffering and haven't accumulated one more corrupt + * work result. */ + if (spare < (int)AVALON_READ_SIZE) + return; + avalon_inc_nvw(info, thr); + } else { + spare = AVALON_READ_SIZE + i; + if (i) { + if (i >= (int)AVALON_READ_SIZE) + avalon_inc_nvw(info, thr); + else + applog(LOG_WARNING, "Avalon: Discarding %d bytes from buffer", i); + } } - avalon->device_fd = fd; - __avalon_init(avalon); + *offset -= spare; + memmove(buf, buf + spare, *offset); +} + +static void avalon_running_reset(struct cgpu_info *avalon, + struct avalon_info *info) +{ + avalon_reset(avalon, false); + avalon_idle(avalon, info); + avalon->results = 0; + info->reset = false; +} + +static void *avalon_get_results(void *userdata) +{ + struct cgpu_info *avalon = (struct cgpu_info *)userdata; + struct avalon_info *info = avalon->device_data; + const int rsize = AVALON_FTDI_READSIZE; + char readbuf[AVALON_READBUF_SIZE]; + struct thr_info *thr = info->thr; + char threadname[24]; + int offset = 0; + + snprintf(threadname, 24, "ava_recv/%d", avalon->device_id); + RenameThread(threadname); + + while (likely(!avalon->shutdown)) { + struct timeval tv_start, now, tdiff; + unsigned char buf[rsize]; + int ret; + + if (offset >= (int)AVALON_READ_SIZE) + avalon_parse_results(avalon, info, thr, readbuf, &offset); + + if (unlikely(offset + rsize >= AVALON_READBUF_SIZE)) { + /* This should never happen */ + applog(LOG_ERR, "Avalon readbuf overflow, resetting buffer"); + offset = 0; + } + + if (unlikely(info->reset)) { + /* Tell the write thread it can start the reset */ + sem_post(&info->write_sem); + sem_wait(&info->read_sem); + + /* Discard anything in the buffer */ + offset = 0; + } + + cgtime(&tv_start); + ret = avalon_read(avalon, buf, rsize, AVALON_READ_TIMEOUT, + C_AVALON_READ); + + if (ret < 1) { + int us_delay; + + cgtime(&now); + timersub(&now, &tv_start, &tdiff); + us_delay = AVALON_READ_TIMEOUT * 1000 - (tdiff.tv_usec); + if (us_delay > 0) + nusleep(us_delay); + continue; + } + + if (opt_debug) { + applog(LOG_DEBUG, "Avalon: get:"); + hexdump((uint8_t *)buf, ret); + } + + memcpy(&readbuf[offset], &buf, ret); + offset += ret; + } + return NULL; +} + +static void avalon_rotate_array(struct cgpu_info *avalon) +{ + avalon->queued = 0; + if (++avalon->work_array >= AVALON_ARRAY_SIZE) + avalon->work_array = 0; +} + +static void *avalon_send_tasks(void *userdata) +{ + struct cgpu_info *avalon = (struct cgpu_info *)userdata; + struct avalon_info *info = avalon->device_data; + const int avalon_get_work_count = info->miner_count; + char threadname[24]; + + snprintf(threadname, 24, "ava_send/%d", avalon->device_id); + RenameThread(threadname); + + while (likely(!avalon->shutdown)) { + int start_count, end_count, i, j, ret; + struct avalon_task at; + int idled = 0; + + wait_avalon_ready(avalon); + + if (unlikely(info->reset)) { + /* Wait till read thread tells us it's received the + * reset message */ + sem_wait(&info->write_sem); + avalon_running_reset(avalon, info); + sem_post(&info->read_sem); + } + + mutex_lock(&info->qlock); + start_count = avalon->work_array * avalon_get_work_count; + end_count = start_count + avalon_get_work_count; + for (i = start_count, j = 0; i < end_count; i++, j++) { + if (avalon_buffer_full(avalon)) { + applog(LOG_INFO, + "AVA%i: Buffer full after only %d of %d work queued", + avalon->device_id, j, avalon_get_work_count); + break; + } + + if (likely(j < avalon->queued)) { + info->idle = false; + avalon_init_task(&at, 0, 0, info->fan_pwm, + info->timeout, info->asic_count, + info->miner_count, 1, 0, info->frequency); + avalon_create_task(&at, avalon->works[i]); + } else { + idled++; + avalon_init_task(&at, 0, 0, info->fan_pwm, + info->timeout, info->asic_count, + info->miner_count, 1, 1, info->frequency); + } + + ret = avalon_send_task(&at, avalon); + + if (unlikely(ret == AVA_SEND_ERROR)) { + applog(LOG_ERR, "AVA%i: Comms error(buffer)", + avalon->device_id); + dev_error(avalon, REASON_DEV_COMMS_ERROR); + info->reset = true; + break; + } + } + + avalon_rotate_array(avalon); + pthread_cond_signal(&info->qcond); + mutex_unlock(&info->qlock); + + if (unlikely(idled && !info->idle)) { + info->idle = true; + applog(LOG_WARNING, "AVA%i: Idled %d miners", + avalon->device_id, idled); + } + } + return NULL; } static bool avalon_prepare(struct thr_info *thr) { struct cgpu_info *avalon = thr->cgpu; - struct avalon_info *info = avalon_infos[avalon->device_id]; + struct avalon_info *info = avalon->device_data; struct timeval now; free(avalon->works); @@ -673,50 +884,38 @@ static bool avalon_prepare(struct thr_info *thr) AVALON_ARRAY_SIZE); if (!avalon->works) quit(1, "Failed to calloc avalon works in avalon_prepare"); - if (avalon->device_fd == -1) - avalon_init(avalon); - else - __avalon_init(avalon); - cgtime(&now); - get_datestamp(avalon->init, &now); - return true; -} + info->thr = thr; + mutex_init(&info->lock); + mutex_init(&info->qlock); + if (unlikely(pthread_cond_init(&info->qcond, NULL))) + quit(1, "Failed to pthread_cond_init avalon qcond"); + if (unlikely(sem_init(&info->read_sem, 0, 0))) + quit(1, "Failed to sem_init avalon read_sem"); + if (unlikely(sem_init(&info->write_sem, 0, 0))) + quit(1, "Failed to sem_init avalon write_sem"); -static void avalon_free_work(struct thr_info *thr) -{ - struct cgpu_info *avalon; - struct avalon_info *info; - struct work **works; - int i; + if (pthread_create(&info->read_thr, NULL, avalon_get_results, (void *)avalon)) + quit(1, "Failed to create avalon read_thr"); - avalon = thr->cgpu; - avalon->queued = 0; - if (unlikely(!avalon->works)) - return; - works = avalon->works; - info = avalon_infos[avalon->device_id]; + if (pthread_create(&info->write_thr, NULL, avalon_send_tasks, (void *)avalon)) + quit(1, "Failed to create avalon write_thr"); - for (i = 0; i < info->miner_count * 4; i++) { - if (works[i]) { - work_completed(avalon, works[i]); - works[i] = NULL; - } - } + avalon_init(avalon); + + cgtime(&now); + get_datestamp(avalon->init, &now); + return true; } static void do_avalon_close(struct thr_info *thr) { - struct avalon_result ar; struct cgpu_info *avalon = thr->cgpu; - struct avalon_info *info = avalon_infos[avalon->device_id]; + struct avalon_info *info = avalon->device_data; - avalon_free_work(thr); - sleep(1); - avalon_reset(avalon->device_fd, &ar); - avalon_idle(avalon); - avalon_close(avalon->device_fd); - avalon->device_fd = -1; + pthread_join(info->read_thr, NULL); + pthread_join(info->write_thr, NULL); + avalon_running_reset(avalon, info); info->no_matching_work = 0; } @@ -771,211 +970,146 @@ static inline void adjust_fan(struct avalon_info *info) } } +static void avalon_update_temps(struct cgpu_info *avalon, struct avalon_info *info, + struct avalon_result *ar) +{ + record_temp_fan(info, ar, &(avalon->temp)); + applog(LOG_INFO, + "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t" + "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC", + info->fan0, info->fan1, info->fan2, + info->temp0, info->temp1, info->temp2, info->temp_max); + info->temp_history_index++; + info->temp_sum += avalon->temp; + applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d", + info->temp_history_index, info->temp_history_count, info->temp_old); + if (info->temp_history_index == info->temp_history_count) { + adjust_fan(info); + info->temp_history_index = 0; + info->temp_sum = 0; + } +} + +static void get_avalon_statline_before(char *buf, struct cgpu_info *avalon) +{ + struct avalon_info *info = avalon->device_data; + int lowfan = 10000, pwm; + + /* Find the lowest fan speed. Fan0 is often not populated. */ + if (info->fan0 > 0) + lowfan = info->fan0; + if (info->fan1 >= 0 && info->fan1 < lowfan) + lowfan = info->fan1; + if (info->fan2 >= 0 && info->fan2 < lowfan) + lowfan = info->fan2; + + pwm = info->fan_pwm * 100 / AVALON_DEFAULT_FAN_MAX_PWM; + + tailsprintf(buf, "%2d/%3dC %3d%%/%04dR| ", info->temp0, info->temp2, + pwm, lowfan); +} + /* We use a replacement algorithm to only remove references to work done from * the buffer when we need the extra space for new work. */ static bool avalon_fill(struct cgpu_info *avalon) { - int subid, slot, mc = avalon_infos[avalon->device_id]->miner_count; + struct avalon_info *info = avalon->device_data; + int subid, slot, mc; struct work *work; + bool ret = true; + mc = info->miner_count; + mutex_lock(&info->qlock); if (avalon->queued >= mc) - return true; + goto out_unlock; work = get_queued(avalon); - if (unlikely(!work)) - return false; + if (unlikely(!work)) { + ret = false; + goto out_unlock; + } subid = avalon->queued++; work->subid = subid; slot = avalon->work_array * mc + subid; if (likely(avalon->works[slot])) work_completed(avalon, avalon->works[slot]); avalon->works[slot] = work; - if (avalon->queued >= mc) - return true; - return false; -} + if (avalon->queued < mc) + ret = false; +out_unlock: + mutex_unlock(&info->qlock); -static void avalon_rotate_array(struct cgpu_info *avalon) -{ - avalon->queued = 0; - if (++avalon->work_array >= AVALON_ARRAY_SIZE) - avalon->work_array = 0; + return ret; } static int64_t avalon_scanhash(struct thr_info *thr) { - struct cgpu_info *avalon; - struct work **works; - int fd, ret = AVA_GETS_OK, full; - - struct avalon_info *info; - struct avalon_task at; - struct avalon_result ar; - int i; - int avalon_get_work_count; - int start_count, end_count; - - struct timeval tv_start, tv_finish, elapsed; - uint32_t nonce; - int64_t hash_count; - static int first_try = 0; - int result_wrong; - - avalon = thr->cgpu; - works = avalon->works; - info = avalon_infos[avalon->device_id]; - avalon_get_work_count = info->miner_count; - - if (unlikely(avalon->device_fd == -1)) { - if (!avalon_prepare(thr)) { - applog(LOG_ERR, "AVA%i: Comms error(open)", - avalon->device_id); - dev_error(avalon, REASON_DEV_COMMS_ERROR); - /* fail the device if the reopen attempt fails */ - return -1; - } - } - fd = avalon->device_fd; -#ifndef WIN32 - tcflush(fd, TCOFLUSH); -#endif - - start_count = avalon->work_array * avalon_get_work_count; - end_count = start_count + avalon_get_work_count; - i = start_count; - while (true) { - avalon_init_task(&at, 0, 0, info->fan_pwm, - info->timeout, info->asic_count, - info->miner_count, 1, 0, info->frequency); - avalon_create_task(&at, works[i]); - ret = avalon_send_task(fd, &at, avalon); - if (unlikely(ret == AVA_SEND_ERROR || - (ret == AVA_SEND_BUFFER_EMPTY && - (i + 1 == end_count) && - first_try))) { - do_avalon_close(thr); - applog(LOG_ERR, "AVA%i: Comms error(buffer)", - avalon->device_id); - dev_error(avalon, REASON_DEV_COMMS_ERROR); - first_try = 0; - sleep(1); - avalon_init(avalon); - return 0; /* This should never happen */ - } - if (ret == AVA_SEND_BUFFER_EMPTY && (i + 1 == end_count)) { - first_try = 1; - avalon_rotate_array(avalon); - return 0xffffffff; - } - - works[i]->blk.nonce = 0xffffffff; - - if (ret == AVA_SEND_BUFFER_FULL) - break; - - i++; - } - if (unlikely(first_try)) - first_try = 0; - - elapsed.tv_sec = elapsed.tv_usec = 0; - cgtime(&tv_start); - - result_wrong = 0; - hash_count = 0; - while (true) { - full = avalon_buffer_full(fd); - applog(LOG_DEBUG, "Avalon: Buffer full: %s", - ((full == AVA_BUFFER_FULL) ? "Yes" : "No")); - if (unlikely(full == AVA_BUFFER_EMPTY)) - break; - - ret = avalon_get_result(fd, &ar, thr, &tv_finish); - if (unlikely(ret == AVA_GETS_ERROR)) { - do_avalon_close(thr); - applog(LOG_ERR, - "AVA%i: Comms error(read)", avalon->device_id); - dev_error(avalon, REASON_DEV_COMMS_ERROR); - return 0; - } - if (unlikely(ret == AVA_GETS_RESTART)) - break; - if (unlikely(ret == AVA_GETS_TIMEOUT)) { - timersub(&tv_finish, &tv_start, &elapsed); - applog(LOG_DEBUG, "Avalon: no nonce in (%ld.%06lds)", - elapsed.tv_sec, elapsed.tv_usec); - continue; - } - - if (!avalon_decode_nonce(thr, &ar, &nonce)) { - info->no_matching_work++; - result_wrong++; - - if (unlikely(result_wrong >= avalon_get_work_count)) - break; - - if (opt_debug) { - timersub(&tv_finish, &tv_start, &elapsed); - applog(LOG_DEBUG,"Avalon: no matching work: %d" - " (%ld.%06lds)", info->no_matching_work, - elapsed.tv_sec, elapsed.tv_usec); - } - continue; - } - - hash_count += 0xffffffff; - if (opt_debug) { - timersub(&tv_finish, &tv_start, &elapsed); - applog(LOG_DEBUG, - "Avalon: nonce = 0x%08x = 0x%08llx hashes " - "(%ld.%06lds)", nonce, (unsigned long long)hash_count, - elapsed.tv_sec, elapsed.tv_usec); - } - } - if (hash_count && avalon->results < AVALON_ARRAY_SIZE) - avalon->results++; - if (unlikely((result_wrong >= avalon_get_work_count) || - (!hash_count && ret != AVA_GETS_RESTART && --avalon->results < 0))) { - /* Look for all invalid results, or consecutive failure - * to generate any results suggesting the FPGA - * controller has screwed up. */ - do_avalon_close(thr); - applog(LOG_ERR, - "AVA%i: FPGA controller messed up, %d wrong results", - avalon->device_id, result_wrong); - dev_error(avalon, REASON_DEV_COMMS_ERROR); - sleep(1); - avalon_init(avalon); - return 0; + struct cgpu_info *avalon = thr->cgpu; + struct avalon_info *info = avalon->device_data; + const int miner_count = info->miner_count; + struct timeval now, then, tdiff; + int64_t hash_count, us_timeout; + struct timespec abstime; + + /* Full nonce range */ + us_timeout = 0x100000000ll / info->asic_count / info->frequency; + tdiff.tv_sec = us_timeout / 1000000; + tdiff.tv_usec = us_timeout - (tdiff.tv_sec * 1000000); + cgtime(&now); + timeradd(&now, &tdiff, &then); + abstime.tv_sec = then.tv_sec; + abstime.tv_nsec = then.tv_usec * 1000; + + /* Wait until avalon_send_tasks signals us that it has completed + * sending its work or a full nonce range timeout has occurred */ + mutex_lock(&info->qlock); + pthread_cond_timedwait(&info->qcond, &info->qlock, &abstime); + mutex_unlock(&info->qlock); + + mutex_lock(&info->lock); + hash_count = 0xffffffffull * (uint64_t)info->nonces; + avalon->results += info->nonces; + if (avalon->results > miner_count) + avalon->results = miner_count; + if (!info->idle && !info->reset) + avalon->results -= miner_count / 3; + else + avalon->results = miner_count; + info->nonces = 0; + mutex_unlock(&info->lock); + + /* Check for nothing but consecutive bad results or consistently less + * results than we should be getting and reset the FPGA if necessary */ + if (avalon->results < -miner_count && !info->reset) { + applog(LOG_ERR, "AVA%d: Result return rate low, resetting!", + avalon->device_id); + info->reset = true; } - avalon_rotate_array(avalon); - - if (hash_count) { - record_temp_fan(info, &ar, &(avalon->temp)); - applog(LOG_INFO, - "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t" - "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC", - info->fan0, info->fan1, info->fan2, - info->temp0, info->temp1, info->temp2, info->temp_max); - info->temp_history_index++; - info->temp_sum += avalon->temp; - applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d", - info->temp_history_index, info->temp_history_count, info->temp_old); - if (info->temp_history_index == info->temp_history_count) { - adjust_fan(info); - info->temp_history_index = 0; - info->temp_sum = 0; - } + if (unlikely(avalon->usbinfo.nodev)) { + applog(LOG_ERR, "AVA%d: Device disappeared, shutting down thread", + avalon->device_id); + avalon->shutdown = true; } /* This hashmeter is just a utility counter based on returned shares */ return hash_count; } +static void avalon_flush_work(struct cgpu_info *avalon) +{ + struct avalon_info *info = avalon->device_data; + + mutex_lock(&info->qlock); + /* Will overwrite any work queued */ + avalon->queued = 0; + pthread_cond_signal(&info->qcond); + mutex_unlock(&info->qlock); +} + static struct api_data *avalon_api_stats(struct cgpu_info *cgpu) { struct api_data *root = NULL; - struct avalon_info *info = avalon_infos[cgpu->device_id]; + struct avalon_info *info = cgpu->device_data; int i; root = api_add_int(root, "baud", &(info->baud), false); @@ -1018,7 +1152,9 @@ struct device_drv avalon_drv = { .hash_work = hash_queued_work, .queue_full = avalon_fill, .scanwork = avalon_scanhash, + .flush_work = avalon_flush_work, .get_api_stats = avalon_api_stats, + .get_statline_before = get_avalon_statline_before, .reinit_device = avalon_init, .thread_shutdown = avalon_shutdown, }; diff --git a/driver-avalon.h b/driver-avalon.h index ed745e77..323a9464 100644 --- a/driver-avalon.h +++ b/driver-avalon.h @@ -12,6 +12,8 @@ #ifdef USE_AVALON +#include + #define AVALON_RESET_FAULT_DECISECONDS 1 #define AVALON_MINER_THREADS 1 @@ -23,11 +25,16 @@ #define AVALON_DEFAULT_FAN_MAX_PWM 0xA0 /* 100% */ #define AVALON_DEFAULT_FAN_MIN_PWM 0x20 /* 20% */ -#define AVALON_DEFAULT_TIMEOUT 0x32 -#define AVALON_DEFAULT_FREQUENCY 256 +#define AVALON_DEFAULT_TIMEOUT 0x2D +#define AVALON_DEFAULT_FREQUENCY 282 #define AVALON_DEFAULT_MINER_NUM 0x20 #define AVALON_DEFAULT_ASIC_NUM 0xA +#define AVALON_FTDI_READSIZE 510 +#define AVALON_READBUF_SIZE 8192 +#define AVALON_RESET_TIMEOUT 100 +#define AVALON_READ_TIMEOUT 10 + struct avalon_task { uint8_t reset :1; uint8_t flush_fifo :1; @@ -95,30 +102,32 @@ struct avalon_info { int matching_work[AVALON_DEFAULT_MINER_NUM]; int frequency; + + struct thr_info *thr; + pthread_t read_thr; + pthread_t write_thr; + pthread_mutex_t lock; + pthread_mutex_t qlock; + pthread_cond_t qcond; + sem_t read_sem; + sem_t write_sem; + int nonces; + + bool idle; + bool reset; }; #define AVALON_WRITE_SIZE (sizeof(struct avalon_task)) #define AVALON_READ_SIZE (sizeof(struct avalon_result)) -#define AVALON_ARRAY_SIZE 4 +#define AVALON_ARRAY_SIZE 3 #define AVA_GETS_ERROR -1 #define AVA_GETS_OK 0 -#define AVA_GETS_RESTART 1 -#define AVA_GETS_TIMEOUT 2 #define AVA_SEND_ERROR -1 #define AVA_SEND_OK 0 -#define AVA_SEND_BUFFER_EMPTY 1 -#define AVA_SEND_BUFFER_FULL 2 - -#define AVA_BUFFER_FULL 0 -#define AVA_BUFFER_EMPTY 1 - -#define avalon_open2(devpath, baud, purge) serial_open(devpath, baud, AVALON_RESET_FAULT_DECISECONDS, purge) -#define avalon_open(devpath, baud) avalon_open2(devpath, baud, true) -#define avalon_close(fd) close(fd) -#define avalon_buffer_full(fd) get_serial_cts(fd) +#define avalon_buffer_full(avalon) !usb_ftdi_cts(avalon) #define AVALON_READ_TIME(baud) ((double)AVALON_READ_SIZE * (double)8.0 / (double)(baud)) #define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] diff --git a/driver-bflsc.c b/driver-bflsc.c index 0e8c57d9..e77d1cc4 100644 --- a/driver-bflsc.c +++ b/driver-bflsc.c @@ -99,11 +99,6 @@ struct bflsc_dev { bool flushed; // are any flushed? }; -// TODO: I stole cgpu_info.device_file -// ... need to update miner.h to instead have a generic void *device_info = NULL; -// ... and these structure definitions need to be in miner.h if API needs to see them -// ... but then again maybe not - maybe another devinfo that the driver provides -// However, clean up all that for all devices in miner.h ... miner.h is a mess at the moment struct bflsc_info { pthread_rwlock_t stat_lock; struct thr_info results_thr; @@ -328,7 +323,7 @@ static void xlinkstr(char *xlink, int dev, struct bflsc_info *sc_info) static void bflsc_applog(struct cgpu_info *bflsc, int dev, enum usb_cmds cmd, int amount, int err) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); char xlink[17]; xlinkstr(xlink, dev, sc_info); @@ -487,7 +482,7 @@ static bool getok(struct cgpu_info *bflsc, enum usb_cmds cmd, int *err, int *amo { char buf[BFLSC_BUFSIZ+1]; - *err = usb_ftdi_read_nl(bflsc, buf, sizeof(buf)-1, amount, cmd); + *err = usb_read_nl(bflsc, buf, sizeof(buf)-1, amount, cmd); if (*err < 0 || *amount < (int)BFLSC_OK_LEN) return false; else @@ -496,7 +491,7 @@ static bool getok(struct cgpu_info *bflsc, enum usb_cmds cmd, int *err, int *amo static bool getokerr(struct cgpu_info *bflsc, enum usb_cmds cmd, int *err, int *amount, char *buf, size_t bufsiz) { - *err = usb_ftdi_read_nl(bflsc, buf, bufsiz-1, amount, cmd); + *err = usb_read_nl(bflsc, buf, bufsiz-1, amount, cmd); if (*err < 0 || *amount < (int)BFLSC_OK_LEN) return false; else { @@ -529,7 +524,7 @@ static void bflsc_send_flush_work(struct cgpu_info *bflsc, int dev) } } -/* return True = attempted usb_ftdi_read_ok() +/* return True = attempted usb_read_ok() * set ignore to true means no applog/ignore errors */ static bool bflsc_qres(struct cgpu_info *bflsc, char *buf, size_t bufsiz, int dev, int *err, int *amount, bool ignore) { @@ -550,7 +545,7 @@ static bool bflsc_qres(struct cgpu_info *bflsc, char *buf, size_t bufsiz, int de // of course all other I/O must also be failing ... } else { readok = true; - *err = usb_ftdi_read_ok(bflsc, buf, bufsiz-1, amount, C_GETRESULTS); + *err = usb_read_ok(bflsc, buf, bufsiz-1, amount, C_GETRESULTS); mutex_unlock(&(bflsc->device_mutex)); if (*err < 0 || *amount < 1) { @@ -568,8 +563,6 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) { int err; -// TODO: this is a standard BFL FPGA Initialisation -// it probably will need changing ... // TODO: does x-link bypass the other device FTDI? (I think it does) // So no initialisation required except for the master device? @@ -588,7 +581,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Set data control err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, - FTDI_VALUE_DATA, bflsc->usbdev->found->interface, C_SETDATA); + FTDI_VALUE_DATA_BAS, bflsc->usbdev->found->interface, C_SETDATA); applog(LOG_DEBUG, "%s%i: setdata got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -597,8 +590,8 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) return; // Set the baud - err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD, - (FTDI_INDEX_BAUD & 0xff00) | bflsc->usbdev->found->interface, + err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_BAS, + (FTDI_INDEX_BAUD_BAS & 0xff00) | bflsc->usbdev->found->interface, C_SETBAUD); applog(LOG_DEBUG, "%s%i: setbaud got err %d", @@ -648,7 +641,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) static void bflsc_initialise(struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); char buf[BFLSC_BUFSIZ+1]; int err, amount; int dev; @@ -665,7 +658,7 @@ static void bflsc_initialise(struct cgpu_info *bflsc) static bool getinfo(struct cgpu_info *bflsc, int dev) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct bflsc_dev sc_dev; char buf[BFLSC_BUFSIZ+1]; int err, amount; @@ -695,7 +688,7 @@ static bool getinfo(struct cgpu_info *bflsc, int dev) return false; } - err = usb_ftdi_read_ok(bflsc, buf, sizeof(buf)-1, &amount, C_GETDETAILS); + err = usb_read_ok(bflsc, buf, sizeof(buf)-1, &amount, C_GETDETAILS); if (err < 0 || amount < 1) { if (err < 0) { applog(LOG_ERR, "%s detect (%s) get details return invalid/timed out (%d:%d)", @@ -805,7 +798,7 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices if (unlikely(!sc_info)) quit(1, "Failed to calloc sc_info in bflsc_detect_one"); // TODO: fix ... everywhere ... - bflsc->device_file = (FILE *)sc_info; + bflsc->device_data = (FILE *)sc_info; if (!usb_init(bflsc, dev, found)) goto shin; @@ -830,7 +823,7 @@ reinit: goto unshin; } - err = usb_ftdi_read_nl(bflsc, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY); + err = usb_read_nl(bflsc, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY); if (err < 0 || amount < 1) { init_count++; cgtime(&init_now); @@ -897,17 +890,20 @@ reinit: sc_info->scan_sleep_time = BAM_SCAN_TIME; sc_info->results_sleep_time = BAM_RES_TIME; sc_info->default_ms_work = BAM_WORK_TIME; + bflsc->usbdev->ident = IDENT_BAM; } else { if (sc_info->sc_devs[0].engines < 34) { // 16 * 2 + 2 newname = BFLSC_JALAPENO; sc_info->scan_sleep_time = BAJ_SCAN_TIME; sc_info->results_sleep_time = BAJ_RES_TIME; sc_info->default_ms_work = BAJ_WORK_TIME; + bflsc->usbdev->ident = IDENT_BAJ; } else if (sc_info->sc_devs[0].engines < 130) { // 16 * 8 + 2 newname = BFLSC_LITTLESINGLE; sc_info->scan_sleep_time = BAL_SCAN_TIME; sc_info->results_sleep_time = BAL_RES_TIME; sc_info->default_ms_work = BAL_WORK_TIME; + bflsc->usbdev->ident = IDENT_BAL; } } @@ -941,7 +937,7 @@ unshin: shin: free(bflsc->device_path); - free(bflsc->device_file); + free(bflsc->device_data); if (bflsc->name != blank) free(bflsc->name); @@ -961,7 +957,7 @@ static void bflsc_detect(void) static void get_bflsc_statline_before(char *buf, struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); float temp = 0; float vcc1 = 0; int i; @@ -982,7 +978,7 @@ static void get_bflsc_statline_before(char *buf, struct cgpu_info *bflsc) static void flush_one_dev(struct cgpu_info *bflsc, int dev) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct work *work, *tmp; bool did = false; @@ -1011,7 +1007,7 @@ static void flush_one_dev(struct cgpu_info *bflsc, int dev) static void bflsc_flush_work(struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); int dev; for (dev = 0; dev < sc_info->sc_count; dev++) @@ -1020,7 +1016,7 @@ static void bflsc_flush_work(struct cgpu_info *bflsc) static void bflsc_flash_led(struct cgpu_info *bflsc, int dev) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); int err, amount; // Device is gone @@ -1051,7 +1047,7 @@ static void bflsc_flash_led(struct cgpu_info *bflsc, int dev) static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct bflsc_dev *sc_dev; char temp_buf[BFLSC_BUFSIZ+1]; char volt_buf[BFLSC_BUFSIZ+1]; @@ -1095,7 +1091,7 @@ static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) return false; } - err = usb_ftdi_read_nl(bflsc, temp_buf, sizeof(temp_buf)-1, &amount, C_GETTEMPERATURE); + err = usb_read_nl(bflsc, temp_buf, sizeof(temp_buf)-1, &amount, C_GETTEMPERATURE); if (err < 0 || amount < 1) { mutex_unlock(&(bflsc->device_mutex)); if (err < 0) { @@ -1117,7 +1113,7 @@ static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) return false; } - err = usb_ftdi_read_nl(bflsc, volt_buf, sizeof(volt_buf)-1, &amount, C_GETTEMPERATURE); + err = usb_read_nl(bflsc, volt_buf, sizeof(volt_buf)-1, &amount, C_GETTEMPERATURE); if (err < 0 || amount < 1) { mutex_unlock(&(bflsc->device_mutex)); if (err < 0) { @@ -1256,7 +1252,7 @@ static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char *data, int count, char **fields, int *nonces) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); char midstate[MIDSTATE_BYTES], blockdata[MERKLE_BYTES]; struct work *work; uint32_t nonce; @@ -1340,7 +1336,7 @@ static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char * static int process_results(struct cgpu_info *bflsc, int dev, char *buf, int *nonces) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); char **items, *firstname, **fields, *lf; int que, i, lines, count; char xlink[17]; @@ -1423,7 +1419,7 @@ arigatou: static void *bflsc_get_results(void *userdata) { struct cgpu_info *bflsc = (struct cgpu_info *)userdata; - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct timeval elapsed, now; float oldest, f; char buf[BFLSC_BUFSIZ+1]; @@ -1488,7 +1484,7 @@ utsura: static bool bflsc_thread_prepare(struct thr_info *thr) { struct cgpu_info *bflsc = thr->cgpu; - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct timeval now; if (thr_info_create(&(sc_info->results_thr), NULL, bflsc_get_results, (void *)bflsc)) { @@ -1506,7 +1502,7 @@ static bool bflsc_thread_prepare(struct thr_info *thr) static void bflsc_shutdown(struct thr_info *thr) { struct cgpu_info *bflsc = thr->cgpu; - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); bflsc_flush_work(bflsc); sc_info->shutdown = true; @@ -1524,7 +1520,7 @@ static void bflsc_thread_enable(struct thr_info *thr) static bool bflsc_send_work(struct cgpu_info *bflsc, int dev, struct work *work) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct FullNonceRangeJob data; char buf[BFLSC_BUFSIZ+1]; int err, amount; @@ -1598,7 +1594,7 @@ re_send: static bool bflsc_queue_full(struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct work *work = NULL; int i, dev, tried, que; bool ret = false; @@ -1666,7 +1662,7 @@ static bool bflsc_queue_full(struct cgpu_info *bflsc) static int64_t bflsc_scanwork(struct thr_info *thr) { struct cgpu_info *bflsc = thr->cgpu; - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); int64_t ret, unsent; bool flushed, cleanup; struct work *work, *tmp; @@ -1773,7 +1769,7 @@ static int64_t bflsc_scanwork(struct thr_info *thr) static bool bflsc_get_stats(struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); bool allok = true; int i; @@ -1798,7 +1794,7 @@ static bool bflsc_get_stats(struct cgpu_info *bflsc) static void bflsc_identify(struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); // TODO: handle x-link sc_info->flash_led = true; @@ -1822,7 +1818,7 @@ static bool bflsc_thread_init(struct thr_info *thr) static struct api_data *bflsc_api_stats(struct cgpu_info *bflsc) { - struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_file); + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct api_data *root = NULL; //if no x-link ... etc diff --git a/driver-bitforce.c b/driver-bitforce.c index f703edcc..c845285e 100644 --- a/driver-bitforce.c +++ b/driver-bitforce.c @@ -101,7 +101,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Set data control err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, - FTDI_VALUE_DATA, bitforce->usbdev->found->interface, C_SETDATA); + FTDI_VALUE_DATA_BFL, bitforce->usbdev->found->interface, C_SETDATA); if (opt_debug) applog(LOG_DEBUG, "%s%i: setdata got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -110,8 +110,8 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) goto failed; // Set the baud - err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD, - (FTDI_INDEX_BAUD & 0xff00) | bitforce->usbdev->found->interface, + err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_BFL, + (FTDI_INDEX_BAUD_BFL & 0xff00) | bitforce->usbdev->found->interface, C_SETBAUD); if (opt_debug) applog(LOG_DEBUG, "%s%i: setbaud got err %d", @@ -200,7 +200,7 @@ reinit: goto unshin; } - if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY)) < 0 || amount < 1) { + if ((err = usb_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY)) < 0 || amount < 1) { init_count++; cgtime(&init_now); if (us_tdiff(&init_now, &init_start) <= REINIT_TIME_MAX) { @@ -395,7 +395,7 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) return false; } - if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETTEMPERATURE)) < 0 || amount < 1) { + if ((err = usb_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETTEMPERATURE)) < 0 || amount < 1) { mutex_unlock(&bitforce->device_mutex); if (err < 0) { applog(LOG_ERR, "%s%i: Error: Get temp return invalid/timed out (%d:%d)", @@ -470,7 +470,7 @@ re_send: return false; } - if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_REQUESTSENDWORKSTATUS)) < 0) { + if ((err = usb_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_REQUESTSENDWORKSTATUS)) < 0) { mutex_unlock(&bitforce->device_mutex); applog(LOG_ERR, "%s%d: read request send work status failed (%d:%d)", bitforce->drv->name, bitforce->device_id, amount, err); @@ -524,7 +524,7 @@ re_send: return false; } - if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_SENDWORKSTATUS)) < 0) { + if ((err = usb_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_SENDWORKSTATUS)) < 0) { mutex_unlock(&bitforce->device_mutex); applog(LOG_ERR, "%s%d: read send work status failed (%d:%d)", bitforce->drv->name, bitforce->device_id, amount, err); @@ -573,7 +573,7 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) mutex_lock(&bitforce->device_mutex); usb_write(bitforce, BITFORCE_WORKSTATUS, BITFORCE_WORKSTATUS_LEN, &amount, C_REQUESTWORKSTATUS); - usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETWORKSTATUS); + usb_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETWORKSTATUS); mutex_unlock(&bitforce->device_mutex); cgtime(&now); diff --git a/driver-icarus.c b/driver-icarus.c index 5d1d91c4..6712a367 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -1,7 +1,7 @@ /* - * Copyright 2012 Luke Dashjr + * Copyright 2012-2013 Andrew Smith * Copyright 2012 Xiangfu - * Copyright 2012 Andrew Smith + * Copyright 2013 Con Kolivas * * 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 @@ -29,30 +29,25 @@ * nonce range is completely calculated. */ -#include "config.h" -#include "miner.h" +#include #include #include +#include #include +#include #include -#include -#include #include -#ifndef WIN32 - #include - #include - #include - #ifndef O_CLOEXEC - #define O_CLOEXEC 0 - #endif -#else - #include - #include + +#include "config.h" + +#ifdef WIN32 +#include #endif -#include "elist.h" -#include "fpgautils.h" +#include "compat.h" +#include "miner.h" +#include "usbutils.h" // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h #define ICARUS_IO_SPEED 115200 @@ -67,41 +62,53 @@ #define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] ASSERT1(sizeof(uint32_t) == 4); -#define ICARUS_READ_TIME(baud) ((double)ICARUS_READ_SIZE * (double)8.0 / (double)(baud)) +// TODO: USB? Different calculation? - see usbstats to work it out e.g. 1/2 of normal send time +// or even use that number? 1/2 +// #define ICARUS_READ_TIME(baud) ((double)ICARUS_READ_SIZE * (double)8.0 / (double)(baud)) +// maybe 1ms? +#define ICARUS_READ_TIME(baud) (0.001) -// Fraction of a second, USB timeout is measured in -// i.e. 10 means 1/10 of a second -#define TIME_FACTOR 10 -// It's 10 per second, thus value = 10/TIME_FACTOR = -#define ICARUS_READ_FAULT_DECISECONDS 1 +// USB ms timeout to wait +#define ICARUS_WAIT_TIMEOUT 100 // In timing mode: Default starting value until an estimate can be obtained -// 5 seconds allows for up to a ~840MH/s device -#define ICARUS_READ_COUNT_TIMING (5 * TIME_FACTOR) +// 5000 ms allows for up to a ~840MH/s device +#define ICARUS_READ_COUNT_TIMING 5000 +#define ICARUS_READ_COUNT_MIN ICARUS_WAIT_TIMEOUT +#define SECTOMS(s) ((int)((s) * 1000)) +// How many ms below the expected completion time to abort work +// extra in case the last read is delayed +#define ICARUS_READ_REDUCE ((int)(ICARUS_WAIT_TIMEOUT * 1.5)) // For a standard Icarus REV3 (to 5 places) // Since this rounds up a the last digit - it is a slight overestimate // Thus the hash rate will be a VERY slight underestimate // (by a lot less than the displayed accuracy) +// Minor inaccuracy of these numbers doesn't affect the work done, +// only the displayed MH/s #define ICARUS_REV3_HASH_TIME 0.0000000026316 +#define LANCELOT_HASH_TIME 0.0000000025000 +#define ASICMINERUSB_HASH_TIME 0.0000000029761 +// TODO: What is it? +#define CAIRNSMORE1_HASH_TIME 0.0000000026316 #define NANOSEC 1000000000.0 // Icarus Rev3 doesn't send a completion message when it finishes // the full nonce range, so to avoid being idle we must abort the -// work (by starting a new work) shortly before it finishes +// work (by starting a new work item) shortly before it finishes // // Thus we need to estimate 2 things: // 1) How many hashes were done if the work was aborted // 2) How high can the timeout be before the Icarus is idle, -// to minimise the number of work started -// We set 2) to 'the calculated estimate' - 1 +// to minimise the number of work items started +// We set 2) to 'the calculated estimate' - ICARUS_READ_REDUCE // to ensure the estimate ends before idle // // The simple calculation used is: // Tn = Total time in seconds to calculate n hashes // Hs = seconds per hash // Xn = number of hashes -// W = code overhead per work +// W = code/usb overhead per work // // Rough but reasonable estimate: // Tn = Hs * Xn + W (of the form y = mx + b) @@ -123,7 +130,7 @@ ASSERT1(sizeof(uint32_t) == 4); #define HISTORY_SEC 60 // Minimum how many points a single ICARUS_HISTORY should have #define MIN_DATA_COUNT 5 -// The value above used is doubled each history until it exceeds: +// The value MIN_DATA_COUNT used is doubled each history until it exceeds: #define MAX_MIN_DATA_COUNT 100 static struct timeval history_sec = { HISTORY_SEC, 0 }; @@ -163,7 +170,8 @@ struct ICARUS_INFO { // seconds per Hash double Hs; - int read_count; + // ms til we abort + int read_time; enum timing_mode timing_mode; bool do_icarus_timing; @@ -188,14 +196,12 @@ struct ICARUS_INFO { #define END_CONDITION 0x0000ffff -// One for each possible device -static struct ICARUS_INFO **icarus_info; - // Looking for options in --icarus-timing and --icarus-options: // // Code increments this each time we start to look at a device // However, this means that if other devices are checked by -// the Icarus code (e.g. BFL) they will count in the option offset +// the Icarus code (e.g. Avalon only as at 20130517) +// they will count in the option offset // // This, however, is deterministic so that's OK // @@ -203,13 +209,173 @@ static struct ICARUS_INFO **icarus_info; // that would be random since an Icarus may fail and thus we'd // not be able to predict the option order // -// This also assumes that serial_detect() checks them sequentially -// and in the order specified on the command line +// Devices are checked in the order libusb finds them which is ? // static int option_offset = -1; struct device_drv icarus_drv; +/* +#define ICA_BUFSIZ (0x200) + +static void transfer_read(struct cgpu_info *icarus, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, char *buf, int bufsiz, int *amount, enum usb_cmds cmd) +{ + int err; + + err = usb_transfer_read(icarus, request_type, bRequest, wValue, wIndex, buf, bufsiz, amount, cmd); + + applog(LOG_DEBUG, "%s: cgid %d %s got err %d", + icarus->drv->name, icarus->cgminer_id, + usb_cmdname(cmd), err); +} +*/ + +static void _transfer(struct cgpu_info *icarus, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, enum usb_cmds cmd) +{ + int err; + + err = usb_transfer_data(icarus, request_type, bRequest, wValue, wIndex, data, siz, cmd); + + applog(LOG_DEBUG, "%s: cgid %d %s got err %d", + icarus->drv->name, icarus->cgminer_id, + usb_cmdname(cmd), err); +} + +#define transfer(icarus, request_type, bRequest, wValue, wIndex, cmd) \ + _transfer(icarus, request_type, bRequest, wValue, wIndex, NULL, 0, cmd) + +static void icarus_initialise(struct cgpu_info *icarus, int baud) +{ + uint16_t wValue, wIndex; + + if (icarus->usbinfo.nodev) + return; + + switch (icarus->usbdev->ident) { + case IDENT_BLT: + case IDENT_LLT: + case IDENT_CMR1: + case IDENT_CMR2: + // Latency + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY, FTDI_VALUE_LATENCY, + icarus->usbdev->found->interface, C_LATENCY); + + if (icarus->usbinfo.nodev) + return; + + // Reset + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_RESET, + icarus->usbdev->found->interface, C_RESET); + + if (icarus->usbinfo.nodev) + return; + + // Set data control + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, FTDI_VALUE_DATA_BLT, + icarus->usbdev->found->interface, C_SETDATA); + + if (icarus->usbinfo.nodev) + return; + + // default to BLT/LLT 115200 + wValue = FTDI_VALUE_BAUD_BLT; + wIndex = FTDI_INDEX_BAUD_BLT; + + if (icarus->usbdev->ident == IDENT_CMR1 || + icarus->usbdev->ident == IDENT_CMR2) { + switch (baud) { + case 115200: + wValue = FTDI_VALUE_BAUD_CMR_115; + wIndex = FTDI_INDEX_BAUD_CMR_115; + break; + case 57600: + wValue = FTDI_VALUE_BAUD_CMR_57; + wIndex = FTDI_INDEX_BAUD_CMR_57; + break; + default: + quit(1, "icarus_intialise() invalid baud (%d) for Cairnsmore1", baud); + break; + } + } + + // Set the baud + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, wValue, + (wIndex & 0xff00) | icarus->usbdev->found->interface, + C_SETBAUD); + + if (icarus->usbinfo.nodev) + return; + + // Set Modem Control + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, FTDI_VALUE_MODEM, + icarus->usbdev->found->interface, C_SETMODEM); + + if (icarus->usbinfo.nodev) + return; + + // Set Flow Control + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, FTDI_VALUE_FLOW, + icarus->usbdev->found->interface, C_SETFLOW); + + if (icarus->usbinfo.nodev) + return; + + // Clear any sent data + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_PURGE_TX, + icarus->usbdev->found->interface, C_PURGETX); + + if (icarus->usbinfo.nodev) + return; + + // Clear any received data + transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_PURGE_RX, + icarus->usbdev->found->interface, C_PURGERX); + + break; + case IDENT_ICA: + // Set Data Control + transfer(icarus, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, PL2303_VALUE_CTRL, + icarus->usbdev->found->interface, C_SETDATA); + + if (icarus->usbinfo.nodev) + return; + + // Set Line Control + uint32_t ica_data[2] = { PL2303_VALUE_LINE0, PL2303_VALUE_LINE1 }; + _transfer(icarus, PL2303_CTRL_OUT, PL2303_REQUEST_LINE, PL2303_VALUE_LINE, + icarus->usbdev->found->interface, + &ica_data[0], PL2303_VALUE_LINE_SIZE, C_SETLINE); + + if (icarus->usbinfo.nodev) + return; + + // Vendor + transfer(icarus, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, PL2303_VALUE_VENDOR, + icarus->usbdev->found->interface, C_VENDOR); + + break; + case IDENT_AMU: + // Set data control + transfer(icarus, CP210X_TYPE_OUT, CP210X_REQUEST_DATA, CP210X_VALUE_DATA, + icarus->usbdev->found->interface, C_SETDATA); + + if (icarus->usbinfo.nodev) + return; + + // Set the baud + uint32_t data = CP210X_DATA_BAUD; + _transfer(icarus, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0, + icarus->usbdev->found->interface, + &data, sizeof(data), C_SETBAUD); + + break; + default: + quit(1, "icarus_intialise() called with invalid %s cgid %i ident=%d", + icarus->drv->name, icarus->cgminer_id, + icarus->usbdev->ident); + } +} + static void rev(unsigned char *s, size_t l) { size_t i, j; @@ -222,79 +388,64 @@ static void rev(unsigned char *s, size_t l) } } -#define icarus_open2(devpath, baud, purge) serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge) -#define icarus_open(devpath, baud) icarus_open2(devpath, baud, false) - -#define ICA_GETS_ERROR -1 -#define ICA_GETS_OK 0 -#define ICA_GETS_RESTART 1 -#define ICA_GETS_TIMEOUT 2 +#define ICA_NONCE_ERROR -1 +#define ICA_NONCE_OK 0 +#define ICA_NONCE_RESTART 1 +#define ICA_NONCE_TIMEOUT 2 -static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count) +static int icarus_get_nonce(struct cgpu_info *icarus, unsigned char *buf, struct timeval *tv_start, struct timeval *tv_finish, struct thr_info *thr, int read_time) { - ssize_t ret = 0; + struct timeval read_start, read_finish; + int err, amt; int rc = 0; int read_amount = ICARUS_READ_SIZE; bool first = true; - // Read reply 1 byte at a time to get earliest tv_finish + cgtime(tv_start); while (true) { - ret = read(fd, buf, 1); - if (ret < 0) - return ICA_GETS_ERROR; + if (icarus->usbinfo.nodev) + return ICA_NONCE_ERROR; + + cgtime(&read_start); + err = usb_read_timeout(icarus, (char *)buf, read_amount, &amt, ICARUS_WAIT_TIMEOUT, C_GETRESULTS); + cgtime(&read_finish); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s%i: Comms error (rerr=%d amt=%d)", + icarus->drv->name, icarus->device_id, err, amt); + dev_error(icarus, REASON_DEV_COMMS_ERROR); + return ICA_NONCE_ERROR; + } if (first) - cgtime(tv_finish); - - if (ret >= read_amount) - return ICA_GETS_OK; - - if (ret > 0) { - buf += ret; - read_amount -= ret; - first = false; - continue; - } - - rc++; - if (rc >= read_count) { - if (opt_debug) { - applog(LOG_DEBUG, - "Icarus Read: No data in %.2f seconds", - (float)rc/(float)TIME_FACTOR); - } - return ICA_GETS_TIMEOUT; + copy_time(tv_finish, &read_finish); + + // TODO: test if there is more data? to read a 2nd nonce? + if (amt >= read_amount) + return ICA_NONCE_OK; + + rc += SECTOMS(tdiff(&read_finish, &read_start)); + if (rc >= read_time) { + if (amt > 0) + applog(LOG_DEBUG, "Icarus Read: Timeout reading for %d ms", rc); + else + applog(LOG_DEBUG, "Icarus Read: No data for %d ms", rc); + return ICA_NONCE_TIMEOUT; } if (thr && thr->work_restart) { if (opt_debug) { applog(LOG_DEBUG, - "Icarus Read: Work restart at %.2f seconds", - (float)(rc)/(float)TIME_FACTOR); + "Icarus Read: Work restart at %d ms", rc); } - return ICA_GETS_RESTART; + return ICA_NONCE_RESTART; } - } -} - -static int icarus_write(int fd, const void *buf, size_t bufLen) -{ - size_t ret; - - ret = write(fd, buf, bufLen); - if (unlikely(ret != bufLen)) - return 1; - - return 0; -} -#define icarus_close(fd) close(fd) - -static void do_icarus_close(struct thr_info *thr) -{ - struct cgpu_info *icarus = thr->cgpu; - icarus_close(icarus->device_fd); - icarus->device_fd = -1; + if (amt > 0) { + buf += amt; + read_amount -= amt; + first = false; + } + } } static const char *timing_mode_str(enum timing_mode timing_mode) @@ -315,7 +466,7 @@ static const char *timing_mode_str(enum timing_mode timing_mode) static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) { - struct ICARUS_INFO *info = icarus_info[icarus->device_id]; + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); double Hs; char buf[BUFSIZ+1]; char *ptr, *comma, *eq; @@ -345,18 +496,37 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) buf[max] = '\0'; } - info->Hs = 0; - info->read_count = 0; + switch (icarus->usbdev->ident) { + case IDENT_ICA: + info->Hs = ICARUS_REV3_HASH_TIME; + break; + case IDENT_BLT: + case IDENT_LLT: + info->Hs = LANCELOT_HASH_TIME; + break; + case IDENT_AMU: + info->Hs = ASICMINERUSB_HASH_TIME; + break; + // TODO: ? + case IDENT_CMR1: + case IDENT_CMR2: + info->Hs = CAIRNSMORE1_HASH_TIME; + break; + default: + quit(1, "Icarus get_options() called with invalid %s ident=%d", + icarus->drv->name, icarus->usbdev->ident); + } + info->read_time = 0; + + // TODO: allow short=N and long=N if (strcasecmp(buf, MODE_SHORT_STR) == 0) { - info->Hs = ICARUS_REV3_HASH_TIME; - info->read_count = ICARUS_READ_COUNT_TIMING; + info->read_time = ICARUS_READ_COUNT_TIMING; info->timing_mode = MODE_SHORT; info->do_icarus_timing = true; } else if (strcasecmp(buf, MODE_LONG_STR) == 0) { - info->Hs = ICARUS_REV3_HASH_TIME; - info->read_count = ICARUS_READ_COUNT_TIMING; + info->read_time = ICARUS_READ_COUNT_TIMING; info->timing_mode = MODE_LONG; info->do_icarus_timing = true; @@ -365,27 +535,29 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) info->fullnonce = info->Hs * (((double)0xffffffff) + 1); if ((eq = strchr(buf, '=')) != NULL) - info->read_count = atoi(eq+1); + info->read_time = atoi(eq+1) * ICARUS_WAIT_TIMEOUT; - if (info->read_count < 1) - info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + if (info->read_time < ICARUS_READ_COUNT_MIN) + info->read_time = SECTOMS(info->fullnonce) - ICARUS_READ_REDUCE; - if (unlikely(info->read_count < 1)) - info->read_count = 1; + if (unlikely(info->read_time < ICARUS_READ_COUNT_MIN)) + info->read_time = ICARUS_READ_COUNT_MIN; info->timing_mode = MODE_VALUE; info->do_icarus_timing = false; } else { // Anything else in buf just uses DEFAULT mode - info->Hs = ICARUS_REV3_HASH_TIME; info->fullnonce = info->Hs * (((double)0xffffffff) + 1); if ((eq = strchr(buf, '=')) != NULL) - info->read_count = atoi(eq+1); + info->read_time = atoi(eq+1) * ICARUS_WAIT_TIMEOUT; + + if (info->read_time < ICARUS_READ_COUNT_MIN) + info->read_time = SECTOMS(info->fullnonce) - ICARUS_READ_REDUCE; - if (info->read_count < 1) - info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + if (unlikely(info->read_time < ICARUS_READ_COUNT_MIN)) + info->read_time = ICARUS_READ_COUNT_MIN; info->timing_mode = MODE_DEFAULT; info->do_icarus_timing = false; @@ -393,8 +565,10 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) info->min_data_count = MIN_DATA_COUNT; - applog(LOG_DEBUG, "Icarus: Init: %d mode=%s read_count=%d Hs=%e", - icarus->device_id, timing_mode_str(info->timing_mode), info->read_count, info->Hs); + applog(LOG_DEBUG, "%s: cgid %d Init: mode=%s read_time=%dms Hs=%e", + icarus->drv->name, icarus->cgminer_id, + timing_mode_str(info->timing_mode), + info->read_time, info->Hs); } static uint32_t mask(int work_division) @@ -424,7 +598,7 @@ static uint32_t mask(int work_division) return nonce_mask; } -static void get_options(int this_option_offset, int *baud, int *work_division, int *fpga_count) +static void get_options(int this_option_offset, struct cgpu_info *icarus, int *baud, int *work_division, int *fpga_count) { char err_buf[BUFSIZ+1]; char buf[BUFSIZ+1]; @@ -455,9 +629,30 @@ static void get_options(int this_option_offset, int *baud, int *work_division, i buf[max] = '\0'; } - *baud = ICARUS_IO_SPEED; - *work_division = 2; - *fpga_count = 2; + switch (icarus->usbdev->ident) { + case IDENT_ICA: + case IDENT_BLT: + case IDENT_LLT: + *baud = ICARUS_IO_SPEED; + *work_division = 2; + *fpga_count = 2; + break; + case IDENT_AMU: + *baud = ICARUS_IO_SPEED; + *work_division = 1; + *fpga_count = 1; + break; + // TODO: ? + case IDENT_CMR1: + case IDENT_CMR2: + *baud = ICARUS_IO_SPEED; + *work_division = 2; + *fpga_count = 2; + break; + default: + quit(1, "Icarus get_options() called with invalid %s ident=%d", + icarus->drv->name, icarus->usbdev->ident); + } if (*buf) { colon = strchr(buf, ':'); @@ -508,13 +703,12 @@ static void get_options(int this_option_offset, int *baud, int *work_division, i } } -static bool icarus_detect_one(const char *devpath) +static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { int this_option_offset = ++option_offset; - + char devpath[20]; struct ICARUS_INFO *info; struct timeval tv_start, tv_finish; - int fd; // Block 171874 nonce = (0xa2870100) = 0x000187a2 // N.B. golden_ob MUST take less time to calculate @@ -528,68 +722,87 @@ static bool icarus_detect_one(const char *devpath) const char golden_nonce[] = "000187a2"; const uint32_t golden_nonce_val = 0x000187a2; - unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; char *nonce_hex; + int baud, uninitialised_var(work_division), uninitialised_var(fpga_count); + struct cgpu_info *icarus; + int ret, err, amount, tries; + bool ok; - int baud, work_division, fpga_count; + icarus = calloc(1, sizeof(struct cgpu_info)); + if (unlikely(!icarus)) + quit(1, "Failed to calloc icarus in icarus_detect_one"); + icarus->drv = &icarus_drv; + icarus->deven = DEV_ENABLED; + icarus->threads = 1; - get_options(this_option_offset, &baud, &work_division, &fpga_count); + if (!usb_init(icarus, dev, found)) + goto shin; - applog(LOG_DEBUG, "Icarus Detect: Attempting to open %s", devpath); + get_options(this_option_offset, icarus, &baud, &work_division, &fpga_count); - fd = icarus_open2(devpath, baud, true); - if (unlikely(fd == -1)) { - applog(LOG_ERR, "Icarus Detect: Failed to open %s", devpath); - return false; - } + sprintf(devpath, "%d:%d", + (int)(icarus->usbinfo.bus_number), + (int)(icarus->usbinfo.device_address)); + + icarus->device_path = strdup(devpath); hex2bin(ob_bin, golden_ob, sizeof(ob_bin)); - icarus_write(fd, ob_bin, sizeof(ob_bin)); - cgtime(&tv_start); - memset(nonce_bin, 0, sizeof(nonce_bin)); - icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1); + tries = 2; + ok = false; + while (!ok && tries-- > 0) { + icarus_initialise(icarus, baud); + + err = usb_write(icarus, (char *)ob_bin, sizeof(ob_bin), &amount, C_SENDTESTWORK); - icarus_close(fd); + if (err != LIBUSB_SUCCESS || amount != sizeof(ob_bin)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, NULL, 100); + if (ret != ICA_NONCE_OK) + continue; - nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); - if (strncmp(nonce_hex, golden_nonce, 8)) { - applog(LOG_ERR, - "Icarus Detect: " - "Test failed at %s: get %s, should: %s", - devpath, nonce_hex, golden_nonce); + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); + if (strncmp(nonce_hex, golden_nonce, 8) == 0) + ok = true; + else { + if (tries < 0) { + applog(LOG_ERR, + "Icarus Detect: " + "Test failed at %s: get %s, should: %s", + devpath, nonce_hex, golden_nonce); + } + } free(nonce_hex); - return false; } + + if (!ok) + goto unshin; + applog(LOG_DEBUG, "Icarus Detect: " "Test succeeded at %s: got %s", - devpath, nonce_hex); - free(nonce_hex); + devpath, golden_nonce); /* We have a real Icarus! */ - struct cgpu_info *icarus; - icarus = calloc(1, sizeof(struct cgpu_info)); - icarus->drv = &icarus_drv; - icarus->device_path = strdup(devpath); - icarus->device_fd = -1; - icarus->threads = 1; - add_cgpu(icarus); - icarus_info = realloc(icarus_info, sizeof(struct ICARUS_INFO *) * (total_devices + 1)); + if (!add_cgpu(icarus)) + goto unshin; - applog(LOG_INFO, "Found Icarus at %s, mark as %d", - devpath, icarus->device_id); + update_usb_stats(icarus); - applog(LOG_DEBUG, "Icarus: Init: %d baud=%d work_division=%d fpga_count=%d", - icarus->device_id, baud, work_division, fpga_count); + applog(LOG_INFO, "%s%d: Found at %s", + icarus->drv->name, icarus->device_id, devpath); - // Since we are adding a new device on the end it needs to always be allocated - icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO)); - if (unlikely(!(icarus_info[icarus->device_id]))) + applog(LOG_DEBUG, "%s%d: Init baud=%d work_division=%d fpga_count=%d", + icarus->drv->name, icarus->device_id, baud, work_division, fpga_count); + + info = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO)); + if (unlikely(!info)) quit(1, "Failed to malloc ICARUS_INFO"); - info = icarus_info[icarus->device_id]; + icarus->device_data = (void *)info; // Initialise everything to zero for a new device memset(info, 0, sizeof(struct ICARUS_INFO)); @@ -605,11 +818,23 @@ static bool icarus_detect_one(const char *devpath) set_timing_mode(this_option_offset, icarus); return true; + +unshin: + + usb_uninit(icarus); + + free(icarus->device_path); + +shin: + + free(icarus); + + return false; } static void icarus_detect() { - serial_detect(&icarus_drv, icarus_detect_one); + usb_detect(&icarus_drv, icarus_detect_one); } static bool icarus_prepare(struct thr_info *thr) @@ -618,18 +843,6 @@ static bool icarus_prepare(struct thr_info *thr) struct timeval now; - icarus->device_fd = -1; - - int fd = icarus_open(icarus->device_path, icarus_info[icarus->device_id]->baud); - if (unlikely(-1 == fd)) { - applog(LOG_ERR, "Failed to open Icarus on %s", - icarus->device_path); - return false; - } - - icarus->device_fd = fd; - - applog(LOG_INFO, "Opened Icarus on %s", icarus->device_path); cgtime(&now); get_datestamp(icarus->init, &now); @@ -639,12 +852,9 @@ static bool icarus_prepare(struct thr_info *thr) static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, __maybe_unused int64_t max_nonce) { - struct cgpu_info *icarus; - int fd; - int ret; - - struct ICARUS_INFO *info; - + struct cgpu_info *icarus = thr->cgpu; + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + int ret, err, amount; unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; char *ob_hex; uint32_t nonce; @@ -658,65 +868,49 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, struct ICARUS_HISTORY *history0, *history; int count; double Hs, W, fullnonce; - int read_count; + int read_time; int64_t estimate_hashes; uint32_t values; int64_t hash_count_range; - elapsed.tv_sec = elapsed.tv_usec = 0; - - icarus = thr->cgpu; - if (icarus->device_fd == -1) - if (!icarus_prepare(thr)) { - applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id); - dev_error(icarus, REASON_DEV_COMMS_ERROR); + // Device is gone + if (icarus->usbinfo.nodev) + return -1; - // fail the device if the reopen attempt fails - return -1; - } - - fd = icarus->device_fd; + elapsed.tv_sec = elapsed.tv_usec = 0; memset(ob_bin, 0, sizeof(ob_bin)); memcpy(ob_bin, work->midstate, 32); memcpy(ob_bin + 52, work->data + 64, 12); rev(ob_bin, 32); rev(ob_bin + 52, 12); -#ifndef WIN32 - tcflush(fd, TCOFLUSH); -#endif - ret = icarus_write(fd, ob_bin, sizeof(ob_bin)); - if (ret) { - do_icarus_close(thr); - applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id); + + err = usb_write(icarus, (char *)ob_bin, sizeof(ob_bin), &amount, C_SENDWORK); + if (err < 0 || amount != sizeof(ob_bin)) { + applog(LOG_ERR, "%s%i: Comms error (werr=%d amt=%d)", + icarus->drv->name, icarus->device_id, err, amount); dev_error(icarus, REASON_DEV_COMMS_ERROR); - return 0; /* This should never happen */ + icarus_initialise(icarus, info->baud); + return 0; } - cgtime(&tv_start); - if (opt_debug) { ob_hex = bin2hex(ob_bin, sizeof(ob_bin)); - applog(LOG_DEBUG, "Icarus %d sent: %s", - icarus->device_id, ob_hex); + applog(LOG_DEBUG, "%s%d: sent %s", + icarus->drv->name, icarus->device_id, ob_hex); free(ob_hex); } /* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */ memset(nonce_bin, 0, sizeof(nonce_bin)); - info = icarus_info[icarus->device_id]; - ret = icarus_gets(nonce_bin, fd, &tv_finish, thr, info->read_count); - if (ret == ICA_GETS_ERROR) { - do_icarus_close(thr); - applog(LOG_ERR, "%s%i: Comms error", icarus->drv->name, icarus->device_id); - dev_error(icarus, REASON_DEV_COMMS_ERROR); + ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, thr, info->read_time); + if (ret == ICA_NONCE_ERROR) return 0; - } work->blk.nonce = 0xffffffff; // aborted before becoming idle, get new work - if (ret == ICA_GETS_TIMEOUT || ret == ICA_GETS_RESTART) { + if (ret == ICA_NONCE_TIMEOUT || ret == ICA_NONCE_RESTART) { timersub(&tv_finish, &tv_start, &elapsed); // ONLY up to just when it aborted @@ -730,8 +924,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, estimate_hashes = 0xffffffff; if (opt_debug) { - applog(LOG_DEBUG, "Icarus %d no nonce = 0x%08lX hashes (%ld.%06lds)", - icarus->device_id, (long unsigned int)estimate_hashes, + applog(LOG_DEBUG, "%s%d: no nonce = 0x%08lX hashes (%ld.%06lds)", + icarus->drv->name, icarus->device_id, + (long unsigned int)estimate_hashes, elapsed.tv_sec, elapsed.tv_usec); } @@ -739,19 +934,11 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, } memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin)); - -#if !defined (__BIG_ENDIAN__) && !defined(MIPSEB) - nonce = swab32(nonce); -#endif - + nonce = htobe32(nonce); curr_hw_errors = icarus->hw_errors; submit_nonce(thr, work, nonce); was_hw_error = (curr_hw_errors > icarus->hw_errors); - // Force a USB close/reopen on any hw error - if (was_hw_error) - do_icarus_close(thr); - hash_count = (nonce & info->nonce_mask); hash_count++; hash_count *= info->fpga_count; @@ -760,8 +947,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, timersub(&tv_finish, &tv_start, &elapsed); if (opt_debug) { - applog(LOG_DEBUG, "Icarus %d nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", - icarus->device_id, nonce, (long unsigned int)hash_count, + applog(LOG_DEBUG, "%s%d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", + icarus->drv->name, icarus->device_id, + nonce, (long unsigned int)hash_count, elapsed.tv_sec, elapsed.tv_usec); } @@ -804,7 +992,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, memset(history0, 0, sizeof(struct ICARUS_HISTORY)); // We just completed a history data set - // So now recalc read_count based on the whole history thus we will + // So now recalc read_time based on the whole history thus we will // initially get more accurate until it completes INFO_HISTORY // total data sets count = 0; @@ -837,10 +1025,10 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, memset(history0, 0, sizeof(struct ICARUS_HISTORY)); fullnonce = W + Hs * (((double)0xffffffff) + 1); - read_count = (int)(fullnonce * TIME_FACTOR) - 1; + read_time = SECTOMS(fullnonce) - ICARUS_READ_REDUCE; info->Hs = Hs; - info->read_count = read_count; + info->read_time = read_time; info->fullnonce = fullnonce; info->count = count; @@ -853,9 +1041,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, else if (info->timing_mode == MODE_SHORT) info->do_icarus_timing = false; -// applog(LOG_WARNING, "Icarus %d Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count); - applog(LOG_WARNING, "Icarus %d Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs", - icarus->device_id, Hs, W, read_count, fullnonce); + applog(LOG_WARNING, "%s%d Re-estimate: Hs=%e W=%e read_time=%dms fullnonce=%.3fs", + icarus->drv->name, icarus->device_id, Hs, W, read_time, fullnonce); } info->history_count++; cgtime(&tv_history_finish); @@ -870,13 +1057,13 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, static struct api_data *icarus_api_stats(struct cgpu_info *cgpu) { struct api_data *root = NULL; - struct ICARUS_INFO *info = icarus_info[cgpu->device_id]; + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data); // Warning, access to these is not locked - but we don't really // care since hashing performance is way more important than // locking access to displaying API debug 'stats' // If locking becomes an issue for any of them, use copy_data=true also - root = api_add_int(root, "read_count", &(info->read_count), false); + root = api_add_int(root, "read_time", &(info->read_time), false); root = api_add_double(root, "fullnonce", &(info->fullnonce), false); root = api_add_int(root, "count", &(info->count), false); root = api_add_hs(root, "Hs", &(info->Hs), false); @@ -896,9 +1083,9 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu) return root; } -static void icarus_shutdown(struct thr_info *thr) +static void icarus_shutdown(__maybe_unused struct thr_info *thr) { - do_icarus_close(thr); + // TODO: ? } struct device_drv icarus_drv = { diff --git a/driver-opencl.c b/driver-opencl.c index d9d9837f..5d622ccf 100644 --- a/driver-opencl.c +++ b/driver-opencl.c @@ -1570,9 +1570,9 @@ static void opencl_thread_shutdown(struct thr_info *thr) const int thr_id = thr->id; _clState *clState = clStates[thr_id]; - clReleaseCommandQueue(clState->commandQueue); clReleaseKernel(clState->kernel); clReleaseProgram(clState->program); + clReleaseCommandQueue(clState->commandQueue); clReleaseContext(clState->context); } diff --git a/driver-ztex.c b/driver-ztex.c index 2ab4a4dd..09387267 100644 --- a/driver-ztex.c +++ b/driver-ztex.c @@ -7,7 +7,7 @@ * Copyright (C) 2009-2011 ZTEX GmbH. * http://www.ztex.de * - * This work is based upon the icarus.c worker which is + * This work is based upon the icarus.c worker which was * Copyright 2012 Luke Dashjr * Copyright 2012 Xiangfu * diff --git a/miner.h b/miner.h index c0771d05..410df533 100644 --- a/miner.h +++ b/miner.h @@ -19,6 +19,10 @@ # include #endif +#ifdef USE_USBUTILS +#include +#endif + #ifdef HAVE_OPENCL #ifdef __APPLE_CC__ #include @@ -422,16 +426,13 @@ struct cgpu_info { int device_id; char *name; char *device_path; - FILE *device_file; + void *device_data; union { #ifdef USE_ZTEX struct libztex_device *device_ztex; #endif #ifdef USE_USBUTILS struct cg_usb_device *usbdev; -#endif -#if defined(USE_ICARUS) || defined(USE_AVALON) - int device_fd; #endif }; #ifdef USE_AVALON @@ -545,6 +546,8 @@ struct cgpu_info { pthread_rwlock_t qlock; struct work *queued_work; unsigned int queued_count; + + bool shutdown; }; extern bool add_cgpu(struct cgpu_info*); @@ -847,6 +850,7 @@ extern char *opt_avalon_options; extern char *opt_usb_select; extern int opt_usbdump; extern bool opt_usb_list_all; +extern sem_t usb_resource_sem; #endif #ifdef USE_BITFORCE extern bool opt_bfl_noncerange; @@ -881,6 +885,7 @@ extern int opt_expiry; #ifdef USE_USBUTILS extern pthread_mutex_t cgusb_lock; +extern pthread_mutex_t cgusbres_lock; #endif extern cglock_t control_lock; @@ -1249,7 +1254,7 @@ struct modminer_fpga_state { extern void get_datestamp(char *, struct timeval *); extern void inc_hw_errors(struct thr_info *thr); -extern void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); +extern bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); extern struct work *get_queued(struct cgpu_info *cgpu); extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); diff --git a/usbtest.py b/usbtest.py new file mode 100755 index 00000000..79f79cbe --- /dev/null +++ b/usbtest.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python2.7 +# +# Original version supplied to me (Kano/kanoi) by xiangfu +# +# Modified to allow supplying the data to send +# +# Linux usAge: ./ubstest.py /dev/ttyUSB0 0xhexcodes|string|icarus +# OR python ubstest.py /dev/ttyUSB0 0xhexcodes|string|icarus +# +# Windows usAge: ./ubstest.py COM1 0xhexcodes|string|icarus +# +# sends the data sepcified to the USB device and waits +# for a reply then displays it +# +# the data can be: +# 0xhexcodes: e.g. 0x68656c6c6f20776f726c640a +# would send "hello world\n" +# +# string: e.g. sendsometext +# +# icarus: sends 2 known block payloads for an icarus device +# and shows the expected and actual answers if it's +# a working V3 icarus + +import sys +import serial +import binascii + +if len(sys.argv) < 2: + sys.stderr.write("usAge: " + sys.argv[0] + " device strings...\n") + sys.stderr.write(" where device is either like /dev/ttyUSB0 or COM1\n") + sys.stderr.write(" and strings are either '0xXXXX' or 'text'\n") + sys.stderr.write(" if the first string is 'icarus' the rest are ignored\n") + sys.stderr.write(" and 2 valid icarus test payloads are sent with results displayed\n") + sys.stderr.write("\nAfter any command is sent it waits up to 30 seconds for a reply\n"); + sys.exit("Aborting") + +# Open with a 10 second timeout - just to be sure +ser = serial.Serial(sys.argv[1], 115200, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, 10, False, False, 5, False, None) + +if sys.argv[2] == "icarus": + + # This show how Icarus use the block and midstate data + # This will produce nonce 063c5e01 + block = "0000000120c8222d0497a7ab44a1a2c7bf39de941c9970b1dc7cdc400000079700000000e88aabe1f353238c668d8a4df9318e614c10c474f8cdf8bc5f6397b946c33d7c4e7242c31a098ea500000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" + midstate = "33c5bf5751ec7f7e056443b5aee3800331432c83f404d9de38b94ecbf907b92d" + + rdata2 = block.decode('hex')[95:63:-1] + rmid = midstate.decode('hex')[::-1] + payload = rmid + rdata2 + + print("Push payload to icarus: " + binascii.hexlify(payload)) + ser.write(payload) + + b=ser.read(4) + print("Result:(should be: 063c5e01): " + binascii.hexlify(b)) + + # Just another test + payload2 = "ce92099c5a80bb81c52990d5c0924c625fd25a535640607d5a4bdf8174e2c8d500000000000000000000000080000000000000000b290c1a42313b4f21b5bcb8" + print("Push payload to icarus: " + payload2) + ser.write(payload2.decode('hex')) + + b=ser.read(4) + print("Result:(should be: 8e0b31c5): " + binascii.hexlify(b)) +else: + data = "" + for arg in sys.argv[2::]: + if arg[0:2:] == '0x': + data += arg[2::].decode('hex') + else: + data += arg + + print("Sending: 0x" + binascii.hexlify(data)) + ser.write(data) + + # If you're expecting more than one linefeed terminated reply, + # you'll only see the first one + # AND with no linefeed, this will wait the 10 seconds before returning + print("Waiting up to 10 seconds ...") + b=ser.readline() + print("Result: hex 0x" + binascii.hexlify(b)) + + # This could mess up the display - do it last + print("Result: asc '" + b + "'") + +ser.close() diff --git a/usbutils.c b/usbutils.c index ae179cbb..2371d488 100644 --- a/usbutils.c +++ b/usbutils.c @@ -49,19 +49,18 @@ #define USB_CONFIG 1 -#define EPI(x) (LIBUSB_ENDPOINT_IN | (unsigned char)(x)) -#define EPO(x) (LIBUSB_ENDPOINT_OUT | (unsigned char)(x)) - #ifdef WIN32 -#define BFLSC_TIMEOUT_MS 500 +#define BFLSC_TIMEOUT_MS 999 #define BITFORCE_TIMEOUT_MS 999 -#define MODMINER_TIMEOUT_MS 200 -#define AVALON_TIMEOUT_MS 500 +#define MODMINER_TIMEOUT_MS 999 +#define AVALON_TIMEOUT_MS 999 +#define ICARUS_TIMEOUT_MS 999 #else #define BFLSC_TIMEOUT_MS 200 #define BITFORCE_TIMEOUT_MS 200 #define MODMINER_TIMEOUT_MS 100 #define AVALON_TIMEOUT_MS 200 +#define ICARUS_TIMEOUT_MS 200 #endif #ifdef USE_BFLSC @@ -94,20 +93,52 @@ static struct usb_endpoints ava_eps[] = { }; #endif -// TODO: Add support for (at least) Isochronous endpoints -static struct usb_find_devices find_dev[] = { -/* #ifdef USE_ICARUS - { DRV_ICARUS, "ICA", 0x067b, 0x0230, true, EPI(3), EPO(2), 1 }, - { DRV_ICARUS, "LOT", 0x0403, 0x6001, false, EPI(0), EPO(0), 1 }, - { DRV_ICARUS, "CM1", 0x067b, 0x0230, false, EPI(0), EPO(0), 1 }, -#endif +static struct usb_endpoints ica_eps[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +}; +static struct usb_endpoints amu_eps[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0 } +}; +static struct usb_endpoints llt_eps[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +}; +static struct usb_endpoints cmr1_eps[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +/* + Interface 1 + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(4), 0 }, + + Interface 2 + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(5), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(6), 0 }, + + Interface 3 + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(7), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(8), 0 } */ +}; +static struct usb_endpoints cmr2_eps[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +}; +#endif + +#define IDVENDOR_FTDI 0x0403 + +// TODO: Add support for (at least) Isochronous endpoints +static struct usb_find_devices find_dev[] = { #ifdef USE_BFLSC { .drv = DRV_BFLSC, .name = "BAS", - .idVendor = 0x0403, + .ident = IDENT_BAS, + .idVendor = IDVENDOR_FTDI, .idProduct = 0x6014, .iManufacturer = "Butterfly Labs", .iProduct = "BitFORCE SHA256 SC", @@ -122,7 +153,8 @@ static struct usb_find_devices find_dev[] = { { .drv = DRV_BITFORCE, .name = "BFL", - .idVendor = 0x0403, + .ident = IDENT_BFL, + .idVendor = IDVENDOR_FTDI, .idProduct = 0x6014, .iManufacturer = "Butterfly Labs Inc.", .iProduct = "BitFORCE SHA256", @@ -137,6 +169,7 @@ static struct usb_find_devices find_dev[] = { { .drv = DRV_MODMINER, .name = "MMQ", + .ident = IDENT_MMQ, .idVendor = 0x1fc9, .idProduct = 0x0003, .kernel = 0, @@ -150,7 +183,8 @@ static struct usb_find_devices find_dev[] = { { .drv = DRV_AVALON, .name = "AVA", - .idVendor = 0x0403, + .ident = IDENT_AVA, + .idVendor = IDVENDOR_FTDI, .idProduct = 0x6001, .kernel = 0, .config = 1, @@ -159,12 +193,91 @@ static struct usb_find_devices find_dev[] = { .epcount = ARRAY_SIZE(ava_eps), .eps = ava_eps }, #endif +#ifdef USE_ICARUS + { + .drv = DRV_ICARUS, + .name = "ICA", + .ident = IDENT_ICA, + .idVendor = 0x067b, + .idProduct = 0x2303, + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = ICARUS_TIMEOUT_MS, + .epcount = ARRAY_SIZE(ica_eps), + .eps = ica_eps }, + { + .drv = DRV_ICARUS, + .name = "AMU", + .ident = IDENT_AMU, + .idVendor = 0x10c4, + .idProduct = 0xea60, + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = ICARUS_TIMEOUT_MS, + .epcount = ARRAY_SIZE(amu_eps), + .eps = amu_eps }, + { + .drv = DRV_ICARUS, + .name = "BLT", + .ident = IDENT_BLT, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .iProduct = "FT232R USB UART", + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = ICARUS_TIMEOUT_MS, + .epcount = ARRAY_SIZE(llt_eps), + .eps = llt_eps }, + // For any that don't match the above "BLT" + { + .drv = DRV_ICARUS, + .name = "LLT", + .ident = IDENT_LLT, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = ICARUS_TIMEOUT_MS, + .epcount = ARRAY_SIZE(llt_eps), + .eps = llt_eps }, + { + .drv = DRV_ICARUS, + .name = "CMR", + .ident = IDENT_CMR1, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x8350, + .iProduct = "Cairnsmore1", + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = ICARUS_TIMEOUT_MS, + .epcount = ARRAY_SIZE(cmr1_eps), + .eps = cmr1_eps }, + { + .drv = DRV_ICARUS, + .name = "CMR", + .ident = IDENT_CMR2, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6014, + .iProduct = "Cairnsmore1", + .kernel = 0, + .config = 1, + .interface = 0, + .timeout = ICARUS_TIMEOUT_MS, + .epcount = ARRAY_SIZE(cmr2_eps), + .eps = cmr2_eps }, +#endif #ifdef USE_ZTEX // This is here so cgminer -n shows them // the ztex driver (as at 201303) doesn't use usbutils { .drv = DRV_ZTEX, .name = "ZTX", + .ident = IDENT_ZTX, .idVendor = 0x221a, .idProduct = 0x0100, .kernel = 0, @@ -174,7 +287,7 @@ static struct usb_find_devices find_dev[] = { .epcount = 0, .eps = NULL }, #endif - { DRV_LAST, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, NULL } + { DRV_LAST, NULL, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, NULL } }; #ifdef USE_BFLSC @@ -202,6 +315,14 @@ static const char *BLANK = ""; static const char *space = " "; static const char *nodatareturned = "no data returned "; +#define IOERR_CHECK(cgpu, err) \ + if (err == LIBUSB_ERROR_IO) { \ + cgpu->usbinfo.ioerr_count++; \ + cgpu->usbinfo.continuous_ioerr_count++; \ + } else { \ + cgpu->usbinfo.continuous_ioerr_count = 0; \ + } + #if 0 // enable USBDEBUG - only during development testing static const char *debug_true_str = "true"; static const char *debug_false_str = "false"; @@ -223,6 +344,8 @@ static struct driver_count { static struct usb_busdev { int bus_number; int device_address; + void *resource1; + void *resource2; } *busdev; static int busdev_count = 0; @@ -240,8 +363,49 @@ struct usb_in_use_list { // List of in use devices static struct usb_in_use_list *in_use_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; + +// Set this to 0 to remove stats processing +#define DO_USB_STATS 1 + static bool stats_initialised = false; +#if DO_USB_STATS + +// 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) + +#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; @@ -255,11 +419,14 @@ struct cg_usb_stats_item { #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; @@ -272,6 +439,17 @@ struct cg_usb_stats { static struct cg_usb_stats *usb_stats = NULL; static int next_stat = 0; +#define USB_STATS(sgpu, sta, fin, err, mode, cmd, seq) \ + stats(cgpu, sta, fin, err, mode, cmd, seq) +#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) +#define STATS_TIMEVAL(tv) +#define USB_REJECT(sgpu, mode) + +#endif // DO_USB_STATS + static const char **usb_commands; static const char *C_REJECTED_S = "RejectedNoDevice"; @@ -321,6 +499,16 @@ static const char *C_QUEJOBSTATUS_S = "QueJobStatus"; static const char *C_QUEFLUSH_S = "QueFlush"; static const char *C_QUEFLUSHREPLY_S = "QueFlushReply"; static const char *C_REQUESTVOLTS_S = "RequestVolts"; +static const char *C_SENDTESTWORK_S = "SendTestWork"; +static const char *C_LATENCY_S = "SetLatency"; +static const char *C_SETLINE_S = "SetLine"; +static const char *C_VENDOR_S = "Vendor"; +static const char *C_AVALON_TASK_S = "AvalonTask"; +static const char *C_AVALON_READ_S = "AvalonRead"; +static const char *C_GET_AVALON_READY_S = "AvalonReady"; +static const char *C_AVALON_RESET_S = "AvalonReset"; +static const char *C_GET_AVALON_RESET_S = "GetAvalonReset"; +static const char *C_FTDI_STATUS_S = "FTDIStatus"; #ifdef EOL #undef EOL @@ -487,6 +675,8 @@ static void append(char **buf, char *append, size_t *off, size_t *len) { *len *= 2; *buf = realloc(*buf, *len); + if (unlikely(!*buf)) + quit(1, "USB failed to realloc append"); } strcpy(*buf + *off, append); @@ -700,6 +890,8 @@ void usb_all(int level) { 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); @@ -738,6 +930,8 @@ static void cgusb_check_init() } usb_commands = malloc(sizeof(*usb_commands) * C_MAX); + if (unlikely(!usb_commands)) + quit(1, "USB failed to malloc usb_commands"); // use constants so the stat generation is very quick // and the association between number and name can't @@ -789,6 +983,16 @@ static void cgusb_check_init() usb_commands[C_QUEFLUSH] = C_QUEFLUSH_S; usb_commands[C_QUEFLUSHREPLY] = C_QUEFLUSHREPLY_S; usb_commands[C_REQUESTVOLTS] = C_REQUESTVOLTS_S; + usb_commands[C_SENDTESTWORK] = C_SENDTESTWORK_S; + usb_commands[C_LATENCY] = C_LATENCY_S; + usb_commands[C_SETLINE] = C_SETLINE_S; + usb_commands[C_VENDOR] = C_VENDOR_S; + usb_commands[C_AVALON_TASK] = C_AVALON_TASK_S; + usb_commands[C_AVALON_READ] = C_AVALON_READ_S; + usb_commands[C_GET_AVALON_READY] = C_GET_AVALON_READY_S; + usb_commands[C_AVALON_RESET] = C_AVALON_RESET_S; + usb_commands[C_GET_AVALON_RESET] = C_GET_AVALON_RESET_S; + usb_commands[C_FTDI_STATUS] = C_FTDI_STATUS_S; stats_initialised = true; } @@ -818,6 +1022,80 @@ void usb_applog(struct cgpu_info *cgpu, enum usb_cmds cmd, char *msg, int amount 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); +} +#endif + static bool __is_in_use(uint8_t bus_number, uint8_t device_address) { struct usb_in_use_list *in_use_tmp; @@ -863,6 +1141,8 @@ static void add_in_use(uint8_t bus_number, uint8_t device_address) } in_use_tmp = calloc(1, sizeof(*in_use_tmp)); + if (unlikely(!in_use_tmp)) + quit(1, "USB failed to calloc 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; @@ -911,251 +1191,88 @@ static void remove_in_use(uint8_t bus_number, uint8_t device_address) (int)bus_number, (int)device_address); } -#ifndef WIN32 -#include -#include -#include -#include -#include -#include -#include -#include - -union semun { - int sem; - struct semid_ds *seminfo; - ushort *all; -}; -#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 cgminer_usb_lock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address) { -#ifdef WIN32 - struct cgpu_info *cgpu; - HANDLE usbMutex; - char name[64]; - DWORD res; - int i; + struct resource_work *res_work; + bool ret; - if (is_in_use_bd(bus_number, device_address)) - return false; + applog(LOG_DEBUG, "USB lock %s %d-%d", drv->dname, (int)bus_number, (int)device_address); - sprintf(name, "cgminer-usb-%d-%d", (int)bus_number, (int)device_address); + res_work = calloc(1, sizeof(*res_work)); + if (unlikely(!res_work)) + quit(1, "USB failed to calloc lock 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; - usbMutex = CreateMutex(NULL, FALSE, name); - if (usbMutex == NULL) { - applog(LOG_ERR, - "MTX: %s USB failed to get '%s' err (%d)", - drv->dname, name, GetLastError()); - return false; - } + mutex_lock(&cgusbres_lock); + res_work->next = res_work_head; + res_work_head = res_work; + mutex_unlock(&cgusbres_lock); - 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", - drv->dname, name); - goto fail; - } - applog(LOG_ERR, - "MTX: %s USB can't get '%s' - device in use - failure (%d)", - drv->dname, name, GetLastError()); - goto fail; - } - } - add_in_use(bus_number, device_address); - return true; - case WAIT_TIMEOUT: - if (!hotplug_mode) - applog(LOG_WARNING, - "MTX: %s USB failed to get '%s' - device in use", - drv->dname, name); - goto fail; - case WAIT_FAILED: - applog(LOG_ERR, - "MTX: %s USB failed to get '%s' err (%d)", - drv->dname, name, GetLastError()); - goto fail; - default: - applog(LOG_ERR, - "MTX: %s USB failed to get '%s' unknown reply (%d)", - drv->dname, name, res); - goto fail; - } + sem_post(&usb_resource_sem); - CloseHandle(usbMutex); - add_in_use(bus_number, device_address); - return true; -fail: - CloseHandle(usbMutex); - return false; -#else - struct semid_ds seminfo; - union semun opt; - char name[64]; - key_t key; - int fd, sem, count; + // TODO: add a timeout fail - restart the resource thread? + while (true) { + nmsleep(50); - if (is_in_use_bd(bus_number, device_address)) - return false; + 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) { - sprintf(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, - "SEM: %s USB open failed '%s' err (%d) %s", - drv->dname, name, errno, strerror(errno)); - return false; - } - close(fd); - key = ftok(name, 'K'); - sem = semget(key, 1, IPC_CREAT | IPC_EXCL | 438); - if (sem < 0) { - if (errno != EEXIST) { - applog(LOG_ERR, - "SEM: %s USB failed to get '%s' err (%d) %s", - drv->dname, name, errno, strerror(errno)); - return false; - } + if (res_reply_prev) + res_reply_prev->next = res_reply->next; + else + res_reply_head = res_reply->next; - sem = semget(key, 1, 0); - if (sem < 0) { - applog(LOG_ERR, - "SEM: %s USB failed to access '%s' err (%d) %s", - drv->dname, name, errno, strerror(errno)); - return false; - } + mutex_unlock(&cgusbres_lock); - opt.seminfo = &seminfo; - count = 0; - while (++count) { - // Should NEVER take 100ms - if (count > 99) { - applog(LOG_ERR, - "SEM: %s USB timeout waiting for (%d) '%s'", - drv->dname, sem, name); - return false; - } - if (semctl(sem, 0, IPC_STAT, opt) == -1) { - applog(LOG_ERR, - "SEM: %s USB failed to wait for (%d) '%s' count %d err (%d) %s", - drv->dname, sem, name, count, errno, strerror(errno)); - return false; + ret = res_reply->got; + + free(res_reply); + + return ret; + } + res_reply_prev = res_reply; + res_reply = res_reply->next; } - if (opt.seminfo->sem_otime != 0) - break; - nmsleep(1); } + mutex_unlock(&cgusbres_lock); } +} - struct sembuf sops[] = { - { 0, 0, IPC_NOWAIT | SEM_UNDO }, - { 0, 1, IPC_NOWAIT | SEM_UNDO } - }; +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)); +} - if (semop(sem, sops, 2)) { - if (errno == EAGAIN) { - if (!hotplug_mode) - applog(LOG_WARNING, - "SEM: %s USB failed to get (%d) '%s' - device in use", - drv->dname, sem, name); - } else { - applog(LOG_DEBUG, - "SEM: %s USB failed to get (%d) '%s' err (%d) %s", - drv->dname, sem, name, errno, strerror(errno)); - } - return false; - } - - add_in_use(bus_number, device_address); - return true; -#endif -} - -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)); -} - -// Any errors should always be printed since they will rarely if ever occur -// and thus it is best to always display them static void cgminer_usb_unlock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address) { -#ifdef WIN32 - HANDLE usbMutex; - char name[64]; - - sprintf(name, "cgminer-usb-%d-%d", (int)bus_number, (int)device_address); - - usbMutex = CreateMutex(NULL, FALSE, name); - if (usbMutex == NULL) { - applog(LOG_ERR, - "MTX: %s USB failed to get '%s' for release err (%d)", - drv->dname, name, GetLastError()); - return; - } - - if (!ReleaseMutex(usbMutex)) - applog(LOG_ERR, - "MTX: %s USB failed to release '%s' err (%d)", - drv->dname, name, GetLastError()); - - CloseHandle(usbMutex); - remove_in_use(bus_number, device_address); - return; -#else - char name[64]; - key_t key; - int fd, sem; + struct resource_work *res_work; - sprintf(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, - "SEM: %s USB open failed '%s' for release err (%d) %s", - drv->dname, name, errno, strerror(errno)); - return; - } - close(fd); - key = ftok(name, 'K'); - - sem = semget(key, 1, 0); - if (sem < 0) { - applog(LOG_ERR, - "SEM: %s USB failed to get '%s' for release err (%d) %s", - drv->dname, name, errno, strerror(errno)); - return; - } + applog(LOG_DEBUG, "USB unlock %s %d-%d", drv->dname, (int)bus_number, (int)device_address); - struct sembuf sops[] = { - { 0, -1, SEM_UNDO } - }; + res_work = calloc(1, sizeof(*res_work)); + if (unlikely(!res_work)) + quit(1, "USB failed to calloc unlock 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; - // Allow a 10ms timeout - // exceeding this timeout means it would probably never succeed anyway - struct timespec timeout = { 0, 10000000 }; + mutex_lock(&cgusbres_lock); + res_work->next = res_work_head; + res_work_head = res_work; + mutex_unlock(&cgusbres_lock); - // Wait forever since we shoud be the one who has it - if (semtimedop(sem, sops, 1, &timeout)) { - applog(LOG_ERR, - "SEM: %s USB failed to release '%s' err (%d) %s", - drv->dname, name, errno, strerror(errno)); - } + sem_post(&usb_resource_sem); - remove_in_use(bus_number, device_address); return; -#endif } static void cgminer_usb_unlock(struct device_drv *drv, libusb_device *dev) @@ -1165,6 +1282,8 @@ static void cgminer_usb_unlock(struct device_drv *drv, libusb_device *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); @@ -1185,6 +1304,9 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) void usb_uninit(struct cgpu_info *cgpu) { + applog(LOG_DEBUG, "USB uninit %s%i", + cgpu->drv->name, cgpu->device_id); + // May have happened already during a failed initialisation // if release_cgpu() was called due to a USB NODEV(err) if (!cgpu->usbdev) @@ -1200,6 +1322,9 @@ static void release_cgpu(struct cgpu_info *cgpu) struct cgpu_info *lookcgpu; int i; + applog(LOG_DEBUG, "USB release %s%i", + cgpu->drv->name, cgpu->device_id); + // It has already been done if (cgpu->usbinfo.nodev) return; @@ -1254,9 +1379,18 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); cgusb = calloc(1, sizeof(*cgusb)); + if (unlikely(!cgusb)) + quit(1, "USB failed to calloc _usb_init cgusb"); cgusb->found = found; + if (found->idVendor == IDVENDOR_FTDI) + cgusb->usb_type = USB_TYPE_FTDI; + + cgusb->ident = found->ident; + cgusb->descriptor = calloc(1, sizeof(*(cgusb->descriptor))); + if (unlikely(!cgusb->descriptor)) + quit(1, "USB failed to calloc _usb_init cgusb descriptor"); err = libusb_get_device_descriptor(dev, cgusb->descriptor); if (err) { @@ -1557,6 +1691,8 @@ static struct usb_find_devices *usb_check_each(int drvnum, struct device_drv *dr if (find_dev[i].drv == drvnum) { if (usb_check_device(drv, dev, &(find_dev[i]))) { found = malloc(sizeof(*found)); + if (unlikely(!found)) + quit(1, "USB failed to malloc found"); memcpy(found, &(find_dev[i]), sizeof(*found)); return found; } @@ -1584,16 +1720,16 @@ static struct usb_find_devices *usb_check(__maybe_unused struct device_drv *drv, return usb_check_each(DRV_BITFORCE, drv, dev); #endif -#ifdef USE_ICARUS - if (drv->drv_id == DRIVER_ICARUS) - return usb_check_each(DRV_ICARUS, drv, dev); -#endif - #ifdef USE_MODMINER if (drv->drv_id == DRIVER_MODMINER) return usb_check_each(DRV_MODMINER, drv, dev); #endif +#ifdef USE_ICARUS + if (drv->drv_id == DRIVER_ICARUS) + return usb_check_each(DRV_ICARUS, drv, dev); +#endif + #ifdef USE_AVALON if (drv->drv_id == DRIVER_AVALON) return usb_check_each(DRV_AVALON, drv, dev); @@ -1662,15 +1798,45 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi libusb_free_device_list(list, 1); } -// Set this to 0 to remove stats processing -#define DO_USB_STATS 1 - #if DO_USB_STATS -#define USB_STATS(sgpu, sta, fin, err, cmd, seq) stats(cgpu, sta, fin, err, cmd, seq) -#define STATS_TIMEVAL(tv) cgtime(tv) -#else -#define USB_STATS(sgpu, sta, fin, err, cmd, seq) -#define STATS_TIMEVAL(tv) +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 - @@ -1685,6 +1851,7 @@ struct api_data *api_usb_stats(__maybe_unused int *count) struct api_data *root = NULL; int device; int cmdseq; + char modes_s[32]; if (next_stat == 0) return NULL; @@ -1708,6 +1875,8 @@ struct api_data *api_usb_stats(__maybe_unused int *count) 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", @@ -1761,10 +1930,15 @@ static void newstats(struct cgpu_info *cgpu) mutex_unlock(&cgusb_lock); usb_stats = realloc(usb_stats, sizeof(*usb_stats) * next_stat); + if (unlikely(!usb_stats)) + quit(1, "USB failed to realloc usb_stats %d", next_stat); usb_stats[next_stat-1].name = cgpu->drv->name; usb_stats[next_stat-1].device_id = -1; usb_stats[next_stat-1].details = calloc(1, sizeof(struct cg_usb_stats_details) * C_MAX * 2); + if (unlikely(!usb_stats[next_stat-1].details)) + quit(1, "USB failed to calloc details for %d", next_stat); + for (i = 1; i < C_MAX * 2; i += 2) usb_stats[next_stat-1].details[i].seq = 1; } @@ -1782,7 +1956,7 @@ void update_usb_stats(__maybe_unused struct cgpu_info *cgpu) } #if DO_USB_STATS -static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timeval *tv_finish, int err, enum usb_cmds cmd, int seq) +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) { struct cg_usb_stats_details *details; double diff; @@ -1792,6 +1966,7 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev newstats(cgpu); details = &(usb_stats[cgpu->usbinfo.usbstat - 1].details[cmd * 2 + seq]); + details->modes |= mode; diff = tdiff(tv_finish, tv_start); @@ -1821,7 +1996,7 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev details->item[item].count++; } -static void rejected_inc(struct cgpu_info *cgpu) +static void rejected_inc(struct cgpu_info *cgpu, uint32_t mode) { struct cg_usb_stats_details *details; int item = CMD_ERROR; @@ -1830,16 +2005,17 @@ static void rejected_inc(struct cgpu_info *cgpu) newstats(cgpu); details = &(usb_stats[cgpu->usbinfo.usbstat - 1].details[C_REJECTED * 2 + 0]); - + details->modes |= mode; details->item[item].count++; } #endif #define USB_MAX_READ 8192 -int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, enum usb_cmds cmd, bool ftdi, bool readonce) +int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, __maybe_unused enum usb_cmds cmd, bool readonce) { struct cg_usb_device *usbdev = cgpu->usbdev; + bool ftdi; #if DO_USB_STATS struct timeval tv_start; #endif @@ -1847,7 +2023,7 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro unsigned int initial_timeout; double max, done; int bufleft, err, got, tot; - bool first = true; + __maybe_unused bool first = true; unsigned char *search; int endlen; @@ -1855,20 +2031,21 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro unsigned char usbbuf[USB_MAX_READ+4], *ptr; size_t usbbufread; - USBDEBUG("USB debug: _usb_read(%s (nodev=%s),ep=%d,buf=%p,bufsiz=%zu,proc=%p,timeout=%u,end=%s,cmd=%s,ftdi=%s,readonce=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, buf, 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, bufsiz, USB_MAX_READ); - if (cgpu->usbinfo.nodev) { *buf = '\0'; *processed = 0; -#if DO_USB_STATS - rejected_inc(cgpu); -#endif + USB_REJECT(cgpu, MODE_BULK_READ); + return LIBUSB_ERROR_NO_DEVICE; } + ftdi = (usbdev->usb_type == USB_TYPE_FTDI); + + USBDEBUG("USB debug: _usb_read(%s (nodev=%s),ep=%d,buf=%p,bufsiz=%zu,proc=%p,timeout=%u,end=%s,cmd=%s,ftdi=%s,readonce=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, buf, 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, bufsiz, USB_MAX_READ); + if (timeout == DEVTIMEOUT) timeout = usbdev->found->timeout; @@ -1890,12 +2067,15 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro err = libusb_bulk_transfer(usbdev->handle, usbdev->found->eps[ep].ep, ptr, usbbufread, &got, timeout); - STATS_TIMEVAL(&tv_finish); - USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, first ? SEQ0 : SEQ1); + cgtime(&tv_finish); + USB_STATS(cgpu, &tv_start, &tv_finish, err, + MODE_BULK_READ, cmd, first ? SEQ0 : SEQ1); ptr[got] = '\0'; USBDEBUG("USB debug: @_usb_read(%s (nodev=%s)) first=%s err=%d%s got=%d ptr='%s' usbbufread=%zu", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), bool_str(first), err, isnodev(err), got, (char *)str_text((char *)ptr), usbbufread); + IOERR_CHECK(cgpu, err); + if (ftdi) { // first 2 bytes returned are an FTDI status if (got > 2) { @@ -1918,7 +2098,7 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro first = false; done = tdiff(&tv_finish, &read_start); - // N.B. this is return LIBUSB_SUCCESS with whatever size has already been read + // N.B. this is: return LIBUSB_SUCCESS with whatever size has already been read if (unlikely(done >= max)) break; @@ -1953,11 +2133,14 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro usbdev->found->eps[ep].ep, ptr, usbbufread, &got, timeout); cgtime(&tv_finish); - USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, first ? SEQ0 : SEQ1); + USB_STATS(cgpu, &tv_start, &tv_finish, err, + MODE_BULK_READ, cmd, first ? SEQ0 : SEQ1); ptr[got] = '\0'; USBDEBUG("USB debug: @_usb_read(%s (nodev=%s)) first=%s err=%d%s got=%d ptr='%s' usbbufread=%zu", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), bool_str(first), err, isnodev(err), got, (char *)str_text((char *)ptr), usbbufread); + IOERR_CHECK(cgpu, err); + if (ftdi) { // first 2 bytes returned are an FTDI status if (got > 2) { @@ -1998,7 +2181,7 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro first = false; done = tdiff(&tv_finish, &read_start); - // N.B. this is return LIBUSB_SUCCESS with whatever size has already been read + // N.B. this is: return LIBUSB_SUCCESS with whatever size has already been read if (unlikely(done >= max)) break; @@ -2014,37 +2197,122 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro return err; } -int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds cmd) +int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, __maybe_unused enum usb_cmds cmd) { struct cg_usb_device *usbdev = cgpu->usbdev; #if DO_USB_STATS - struct timeval tv_start, tv_finish; + struct timeval tv_start; #endif - int err, sent; + struct timeval read_start, tv_finish; + unsigned int initial_timeout; + double max, done; + __maybe_unused bool first = true; + int err, sent, tot; USBDEBUG("USB debug: _usb_write(%s (nodev=%s),ep=%d,buf='%s',bufsiz=%zu,proc=%p,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, (char *)str_text(buf), bufsiz, processed, timeout, usb_cmdname(cmd)); + *processed = 0; + if (cgpu->usbinfo.nodev) { - *processed = 0; + USB_REJECT(cgpu, MODE_BULK_WRITE); + + return LIBUSB_ERROR_NO_DEVICE; + } + + if (timeout == DEVTIMEOUT) + timeout = usbdev->found->timeout; + + tot = 0; + err = LIBUSB_SUCCESS; + initial_timeout = timeout; + max = ((double)timeout) / 1000.0; + cgtime(&read_start); + while (bufsiz > 0) { + sent = 0; + STATS_TIMEVAL(&tv_start); + err = libusb_bulk_transfer(usbdev->handle, + usbdev->found->eps[ep].ep, + (unsigned char *)buf, + bufsiz, &sent, timeout); + cgtime(&tv_finish); + USB_STATS(cgpu, &tv_start, &tv_finish, err, + MODE_BULK_WRITE, cmd, first ? SEQ0 : SEQ1); + + 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); + + IOERR_CHECK(cgpu, err); + + tot += sent; + + if (err) + break; + + buf += sent; + bufsiz -= sent; + + first = false; + + done = tdiff(&tv_finish, &read_start); + // N.B. this is: return LIBUSB_SUCCESS with whatever size was written + if (unlikely(done >= max)) + break; + + timeout = initial_timeout - (done * 1000); + } + + *processed = tot; + + if (NODEV(err)) + release_cgpu(cgpu); + + 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 = cgpu->usbdev; #if DO_USB_STATS - rejected_inc(cgpu); + struct timeval tv_start, tv_finish; #endif + uint32_t *buf = NULL; + 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); + return LIBUSB_ERROR_NO_DEVICE; } - sent = 0; + USBDEBUG("USB debug: @_usb_transfer() data=%s", bin2hex((unsigned char *)data, (size_t)siz)); + + if (siz > 0) { + bufsiz = siz - 1; + bufsiz >>= 2; + bufsiz++; + buf = malloc(bufsiz << 2); + if (unlikely(!buf)) + quit(1, "Failed to malloc in _usb_transfer"); + for (i = 0; i < bufsiz; i++) + buf[i] = htole32(data[i]); + } + + USBDEBUG("USB debug: @_usb_transfer() buf=%s", bin2hex((unsigned char *)buf, (size_t)siz)); + STATS_TIMEVAL(&tv_start); - err = libusb_bulk_transfer(usbdev->handle, - usbdev->found->eps[ep].ep, - (unsigned char *)buf, - bufsiz, &sent, - timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout); + err = libusb_control_transfer(usbdev->handle, request_type, + bRequest, wValue, wIndex, (unsigned char *)buf, (uint16_t)siz, + timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout); STATS_TIMEVAL(&tv_finish); - USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0); + USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_WRITE, cmd, SEQ0); - 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); + USBDEBUG("USB debug: @_usb_transfer(%s (nodev=%s)) err=%d%s", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err)); + + IOERR_CHECK(cgpu, err); - *processed = sent; + if (buf) + free(buf); if (NODEV(err)) release_cgpu(cgpu); @@ -2052,7 +2320,7 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr return err; } -int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd) +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 = cgpu->usbdev; #if DO_USB_STATS @@ -2060,52 +2328,115 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest #endif int err; - USBDEBUG("USB debug: _usb_transfer(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, timeout, usb_cmdname(cmd)); + 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) { -#if DO_USB_STATS - rejected_inc(cgpu); -#endif + USB_REJECT(cgpu, MODE_CTRL_READ); + return LIBUSB_ERROR_NO_DEVICE; } + *amount = 0; + STATS_TIMEVAL(&tv_start); err = libusb_control_transfer(usbdev->handle, request_type, - bRequest, htole16(wValue), htole16(wIndex), NULL, 0, + bRequest, wValue, wIndex, + (unsigned char *)buf, (uint16_t)bufsiz, timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout); STATS_TIMEVAL(&tv_finish); - USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0); + USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_READ, cmd, SEQ0); - USBDEBUG("USB debug: @_usb_transfer(%s (nodev=%s)) err=%d%s", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err)); + 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 (NODEV(err)) + IOERR_CHECK(cgpu, err); + + if (err > 0) { + *amount = err; + err = 0; + } else if (NODEV(err)) release_cgpu(cgpu); 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); +} + void usb_cleanup() { struct cgpu_info *cgpu; + int count; int i; hotplug_time = 0; nmsleep(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_MODMINER: + case DRIVER_ICARUS: case DRIVER_AVALON: release_cgpu(cgpu); + 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) { + nmsleep(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); + } } void usb_initialise() @@ -2160,6 +2491,8 @@ void usb_initialise() } busdev = realloc(busdev, sizeof(*busdev) * (++busdev_count)); + if (unlikely(!busdev)) + quit(1, "USB failed to realloc busdev"); busdev[busdev_count-1].bus_number = bus; busdev[busdev_count-1].device_address = dev; @@ -2207,6 +2540,12 @@ void usb_initialise() found = true; } #endif +#ifdef USE_ICARUS + if (!found && strcasecmp(ptr, icarus_drv.name) == 0) { + drv_count[icarus_drv.drv_id].limit = lim; + found = true; + } +#endif #ifdef USE_AVALON if (!found && strcasecmp(ptr, avalon_drv.name) == 0) { drv_count[avalon_drv.drv_id].limit = lim; @@ -2222,3 +2561,391 @@ void usb_initialise() } } } + +#ifndef WIN32 +#include +#include +#include +#include +#include +#include +#include +#include + +union semun { + int sem; + struct semid_ds *seminfo; + ushort *all; +}; +#else +static LPSECURITY_ATTRIBUTES unsec(LPSECURITY_ATTRIBUTES sec) +{ + FreeSid(((PSECURITY_DESCRIPTOR)(sec->lpSecurityDescriptor))->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 = malloc(sizeof(*sec_des)); + if (unlikely(!sec_des)) + quit(1, "MTX: Failed to malloc LPSECURITY_DESCRIPTOR"); + + 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, + 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, + 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, + 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, + GetLastError()); + FreeSid(gsid); + free(sec_des); + return NULL; + } + + sec_att = malloc(sizeof(*sec_att)); + if (unlikely(!sec_att)) + quit(1, "MTX: Failed to malloc LPSECURITY_ATTRIBUTES"); + + 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; + + sprintf(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, 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, 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, GetLastError()); + goto fail; + default: + applog(LOG_ERR, + "MTX: %s USB failed to get '%s' unknown reply (%d)", + dname, name, res); + goto fail; + } + + add_in_use(bus_number, device_address); + in_use_store_ress(bus_number, device_address, (void *)usbMutex, (void *)sec); + + return true; +fail: + CloseHandle(usbMutex); + sec = unsec(sec); + return false; +#else + struct semid_ds seminfo; + union semun opt; + char name[64]; + key_t key; + int fd, sem, count; + + if (is_in_use_bd(bus_number, device_address)) + return false; + + sprintf(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, + "SEM: %s USB open failed '%s' err (%d) %s", + dname, name, errno, strerror(errno)); + return false; + } + close(fd); + key = ftok(name, 'K'); + sem = semget(key, 1, IPC_CREAT | IPC_EXCL | 438); + if (sem < 0) { + if (errno != EEXIST) { + applog(LOG_ERR, + "SEM: %s USB failed to get '%s' err (%d) %s", + dname, name, errno, strerror(errno)); + return false; + } + + sem = semget(key, 1, 0); + if (sem < 0) { + applog(LOG_ERR, + "SEM: %s USB failed to access '%s' err (%d) %s", + dname, name, errno, strerror(errno)); + return false; + } + + opt.seminfo = &seminfo; + count = 0; + while (++count) { + // Should NEVER take 100ms + if (count > 99) { + applog(LOG_ERR, + "SEM: %s USB timeout waiting for (%d) '%s'", + dname, sem, name); + return false; + } + if (semctl(sem, 0, IPC_STAT, opt) == -1) { + applog(LOG_ERR, + "SEM: %s USB failed to wait for (%d) '%s' count %d err (%d) %s", + dname, sem, name, count, errno, strerror(errno)); + return false; + } + if (opt.seminfo->sem_otime != 0) + break; + nmsleep(1); + } + } + + struct sembuf sops[] = { + { 0, 0, IPC_NOWAIT | SEM_UNDO }, + { 0, 1, IPC_NOWAIT | SEM_UNDO } + }; + + if (semop(sem, sops, 2)) { + if (errno == EAGAIN) { + if (!hotplug_mode) + applog(LOG_WARNING, + "SEM: %s USB failed to get (%d) '%s' - device in use", + dname, sem, name); + } else { + applog(LOG_DEBUG, + "SEM: %s USB failed to get (%d) '%s' err (%d) %s", + dname, sem, name, errno, strerror(errno)); + } + return false; + } + + add_in_use(bus_number, device_address); + 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]; + + sprintf(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, GetLastError()); + +fila: + + if (usbMutex) + CloseHandle(usbMutex); + if (sec) + unsec(sec); + remove_in_use(bus_number, device_address); + return; +#else + char name[64]; + key_t key; + int fd, sem; + + sprintf(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, + "SEM: %s USB open failed '%s' for release err (%d) %s", + dname, name, errno, strerror(errno)); + return; + } + close(fd); + key = ftok(name, 'K'); + + sem = semget(key, 1, 0); + if (sem < 0) { + applog(LOG_ERR, + "SEM: %s USB failed to get '%s' for release err (%d) %s", + dname, name, errno, strerror(errno)); + return; + } + + struct sembuf sops[] = { + { 0, -1, SEM_UNDO } + }; + + // Allow a 10ms timeout + // exceeding this timeout means it would probably never succeed anyway + struct timespec timeout = { 0, 10000000 }; + + if (semtimedop(sem, sops, 1, &timeout)) { + applog(LOG_ERR, + "SEM: %s USB failed to release '%s' err (%d) %s", + dname, name, errno, strerror(errno)); + } + + remove_in_use(bus_number, device_address); + 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 = calloc(1, sizeof(*res_reply)); + if (unlikely(!res_reply)) + quit(1, "USB failed to calloc 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 */ + sem_wait(&usb_resource_sem); + + mutex_lock(&cgusbres_lock); + while (res_work_head) + resource_process(); + mutex_unlock(&cgusbres_lock); + } + + return NULL; +} diff --git a/usbutils.h b/usbutils.h index 2c4f6424..a7b79d1f 100644 --- a/usbutils.h +++ b/usbutils.h @@ -12,29 +12,88 @@ #include -// for 0x0403/0x6014 FT232H (and possibly others?) +#define EPI(x) (LIBUSB_ENDPOINT_IN | (unsigned char)(x)) +#define EPO(x) (LIBUSB_ENDPOINT_OUT | (unsigned char)(x)) + + +// For 0x0403:0x6014/0x6001 FT232H (and possibly others?) - BFL, BAS, BLT, LLT, AVA #define FTDI_TYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT) +#define FTDI_TYPE_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN) #define FTDI_REQUEST_RESET ((uint8_t)0) #define FTDI_REQUEST_MODEM ((uint8_t)1) #define FTDI_REQUEST_FLOW ((uint8_t)2) #define FTDI_REQUEST_BAUD ((uint8_t)3) #define FTDI_REQUEST_DATA ((uint8_t)4) +#define FTDI_REQUEST_LATENCY ((uint8_t)9) #define FTDI_VALUE_RESET 0 #define FTDI_VALUE_PURGE_RX 1 #define FTDI_VALUE_PURGE_TX 2 +#define FTDI_VALUE_LATENCY 1 + +// Baud +#define FTDI_VALUE_BAUD_BFL 0xc068 +#define FTDI_INDEX_BAUD_BFL 0x0200 +#define FTDI_VALUE_BAUD_BAS FTDI_VALUE_BAUD_BFL +#define FTDI_INDEX_BAUD_BAS FTDI_INDEX_BAUD_BFL +// LLT = BLT (same code) +#define FTDI_VALUE_BAUD_BLT 0x001a +#define FTDI_INDEX_BAUD_BLT 0x0000 + +// Avalon +#define FTDI_VALUE_BAUD_AVA 0x001A +#define FTDI_INDEX_BAUD_AVA 0x0000 + +#define FTDI_VALUE_DATA_AVA 8 + +// CMR = 115200 & 57600 +#define FTDI_VALUE_BAUD_CMR_115 0xc068 +#define FTDI_INDEX_BAUD_CMR_115 0x0200 + +#define FTDI_VALUE_BAUD_CMR_57 0x80d0 +#define FTDI_INDEX_BAUD_CMR_57 0x0200 -// baud with a 0 divisor is 120,000,000/10 -//#define FTDI_VALUE_BAUD (0) -//#define FTDI_INDEX_BAUD (0) -#define FTDI_VALUE_BAUD 0xc068 -#define FTDI_INDEX_BAUD 0x0200 +// Data control +#define FTDI_VALUE_DATA_BFL 0 +#define FTDI_VALUE_DATA_BAS FTDI_VALUE_DATA_BFL +// LLT = BLT (same code) +#define FTDI_VALUE_DATA_BLT 8 -#define FTDI_VALUE_DATA 0 #define FTDI_VALUE_FLOW 0 #define FTDI_VALUE_MODEM 0x0303 + +// For 0x10c4:0xea60 USB cp210x chip - AMU +#define CP210X_TYPE_OUT 0x41 + +#define CP210X_REQUEST_DATA 0x07 +#define CP210X_REQUEST_BAUD 0x1e + +#define CP210X_VALUE_DATA 0x0303 +#define CP210X_DATA_BAUD 0x0001c200 + + +// For 0x067b:0x2303 Prolific PL2303 - ICA +#define PL2303_CTRL_DTR 0x01 +#define PL2303_CTRL_RTS 0x02 + +#define PL2303_CTRL_OUT 0x21 +#define PL2303_VENDOR_OUT 0x40 + +#define PL2303_REQUEST_CTRL 0x22 +#define PL2303_REQUEST_LINE 0x20 +#define PL2303_REQUEST_VENDOR 0x01 + +#define PL2303_REPLY_CTRL 0x21 + +#define PL2303_VALUE_CTRL (PL2303_CTRL_DTR | PL2303_CTRL_RTS) +#define PL2303_VALUE_LINE 0 +#define PL2303_VALUE_LINE0 0x0001c200 +#define PL2303_VALUE_LINE1 0x080000 +#define PL2303_VALUE_LINE_SIZE 7 +#define PL2303_VALUE_VENDOR 0 + // Use the device defined timeout #define DEVTIMEOUT 0 @@ -50,9 +109,28 @@ struct usb_endpoints { bool found; }; +enum sub_ident { + IDENT_UNK = 0, + IDENT_BAJ, + IDENT_BAL, + IDENT_BAS, + IDENT_BAM, + IDENT_BFL, + IDENT_MMQ, + IDENT_AVA, + IDENT_ICA, + IDENT_AMU, + IDENT_BLT, + IDENT_LLT, + IDENT_CMR1, + IDENT_CMR2, + IDENT_ZTX +}; + struct usb_find_devices { int drv; const char *name; + enum sub_ident ident; uint16_t idVendor; uint16_t idProduct; char *iManufacturer; @@ -65,11 +143,18 @@ struct usb_find_devices { struct usb_endpoints *eps; }; +enum usb_types { + USB_TYPE_STD = 0, + USB_TYPE_FTDI +}; + struct cg_usb_device { struct usb_find_devices *found; libusb_device_handle *handle; pthread_mutex_t *mutex; struct libusb_device_descriptor *descriptor; + enum usb_types usb_type; + enum sub_ident ident; uint16_t usbver; int speed; char *prod_string; @@ -86,6 +171,8 @@ struct cg_usb_info { bool nodev; int nodev_count; struct timeval last_nodev; + uint32_t ioerr_count; + uint32_t continuous_ioerr_count; }; enum usb_cmds { @@ -136,6 +223,16 @@ enum usb_cmds { C_QUEFLUSH, C_QUEFLUSHREPLY, C_REQUESTVOLTS, + C_SENDTESTWORK, + C_LATENCY, + C_SETLINE, + C_VENDOR, + C_AVALON_TASK, + C_AVALON_READ, + C_GET_AVALON_READY, + C_AVALON_RESET, + C_GET_AVALON_RESET, + C_FTDI_STATUS, C_MAX }; @@ -150,29 +247,39 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)); struct api_data *api_usb_stats(int *count); void update_usb_stats(struct cgpu_info *cgpu); -int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, enum usb_cmds cmd, bool ftdi, bool readonce); +int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, enum usb_cmds cmd, bool readonce); +int usb_ftdi_cts(struct cgpu_info *cgpu); +int usb_ftdi_ctw(struct cgpu_info *cgpu); int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds); -int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd); +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 _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, enum usb_cmds cmd); void usb_cleanup(); void usb_initialise(); +void *usb_resource_thread(void *userdata); #define usb_read(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, false) + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false) #define usb_read_once(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, true) + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, true) + +#define usb_read_once_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, true) #define usb_read_nl(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "\n", cmd, false, false) + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "\n", cmd, false) + +#define usb_read_ok(cgpu, buf, bufsiz, read, cmd) \ + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "OK\n", cmd, false) #define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, false) + _usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false) #define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false, false) + _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false) #define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, ep, buf, bufsiz, read, timeout, NULL, cmd, false, false) + _usb_read(cgpu, ep, buf, bufsiz, read, timeout, NULL, cmd, false) #define usb_write(cgpu, buf, bufsiz, wrote, cmd) \ _usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd) @@ -186,16 +293,13 @@ void usb_initialise(); #define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \ _usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) -#define usb_ftdi_read(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, true, false) - -#define usb_ftdi_read_nl(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "\n", cmd, true, false) +#define usb_transfer(cgpu, typ, req, val, idx, cmd) \ + _usb_transfer(cgpu, typ, req, val, idx, NULL, 0, DEVTIMEOUT, cmd) -#define usb_ftdi_read_ok(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "OK\n", cmd, true, false) +#define usb_transfer_data(cgpu, typ, req, val, idx, data, len, cmd) \ + _usb_transfer(cgpu, typ, req, val, idx, data, len, DEVTIMEOUT, cmd) -#define usb_transfer(cgpu, typ, req, val, idx, cmd) \ - _usb_transfer(cgpu, typ, req, val, idx, DEVTIMEOUT, cmd) +#define usb_transfer_read(cgpu, typ, req, val, idx, buf, bufsiz, read, cmd) \ + _usb_transfer_read(cgpu, typ, req, val, idx, buf, bufsiz, read, DEVTIMEOUT, cmd) #endif diff --git a/util.c b/util.c index 07669e3f..14334771 100644 --- a/util.c +++ b/util.c @@ -844,6 +844,29 @@ void nmsleep(unsigned int msecs) #endif } +/* Same for usecs */ +void nusleep(unsigned int usecs) +{ + struct timespec twait, tleft; + int ret; + ldiv_t d; + +#ifdef WIN32 + timeBeginPeriod(1); +#endif + d = ldiv(usecs, 1000000); + tleft.tv_sec = d.quot; + tleft.tv_nsec = d.rem * 1000; + do { + twait.tv_sec = tleft.tv_sec; + twait.tv_nsec = tleft.tv_nsec; + ret = nanosleep(&twait, &tleft); + } while (ret == -1 && errno == EINTR); +#ifdef WIN32 + timeEndPeriod(1); +#endif +} + /* This is a cgminer gettimeofday wrapper. Since we always call gettimeofday * with tz set to NULL, and windows' default resolution is only 15ms, this * gives us higher resolution times on windows. */ diff --git a/util.h b/util.h index be163188..5facbc5a 100644 --- a/util.h +++ b/util.h @@ -58,6 +58,7 @@ int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) ( void thr_info_freeze(struct thr_info *thr); void thr_info_cancel(struct thr_info *thr); void nmsleep(unsigned int msecs); +void nusleep(unsigned int usecs); void cgtime(struct timeval *tv); void subtime(struct timeval *a, struct timeval *b); void addtime(struct timeval *a, struct timeval *b);