|
|
|
@ -110,24 +110,10 @@ extern struct device_drv icarus_drv;
@@ -110,24 +110,10 @@ extern struct device_drv icarus_drv;
|
|
|
|
|
extern struct device_drv modminer_drv; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Our own internal list of used USB devices |
|
|
|
|
* So two drivers or a single driver searching |
|
|
|
|
* can't touch the same device during detection |
|
|
|
|
*/ |
|
|
|
|
struct usb_list { |
|
|
|
|
uint8_t bus_number; |
|
|
|
|
uint8_t device_address; |
|
|
|
|
uint8_t filler[2]; |
|
|
|
|
struct usb_list *prev; |
|
|
|
|
struct usb_list *next; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#define STRBUFLEN 256 |
|
|
|
|
static const char *BLANK = ""; |
|
|
|
|
|
|
|
|
|
static pthread_mutex_t *list_lock = NULL; |
|
|
|
|
static struct usb_list *usb_head = NULL; |
|
|
|
|
static bool stats_initialised = false; |
|
|
|
|
|
|
|
|
|
struct cg_usb_stats_item { |
|
|
|
|
uint64_t count; |
|
|
|
@ -537,10 +523,7 @@ static void cgusb_check_init()
@@ -537,10 +523,7 @@ static void cgusb_check_init()
|
|
|
|
|
{ |
|
|
|
|
mutex_lock(&cgusb_lock); |
|
|
|
|
|
|
|
|
|
if (list_lock == NULL) { |
|
|
|
|
list_lock = calloc(1, sizeof(*list_lock)); |
|
|
|
|
mutex_init(list_lock); |
|
|
|
|
|
|
|
|
|
if (stats_initialised == false) { |
|
|
|
|
// N.B. environment LIBUSB_DEBUG also sets libusb_set_debug()
|
|
|
|
|
if (opt_usbdump >= 0) { |
|
|
|
|
libusb_set_debug(NULL, opt_usbdump); |
|
|
|
@ -592,134 +575,163 @@ static void cgusb_check_init()
@@ -592,134 +575,163 @@ static void cgusb_check_init()
|
|
|
|
|
mutex_unlock(&cgusb_lock); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool in_use(libusb_device *dev, bool lock) |
|
|
|
|
{ |
|
|
|
|
struct usb_list *usb_tmp; |
|
|
|
|
bool used = false; |
|
|
|
|
uint8_t bus_number; |
|
|
|
|
uint8_t device_address; |
|
|
|
|
|
|
|
|
|
bus_number = libusb_get_bus_number(dev); |
|
|
|
|
device_address = libusb_get_device_address(dev); |
|
|
|
|
|
|
|
|
|
if (lock) |
|
|
|
|
mutex_lock(list_lock); |
|
|
|
|
|
|
|
|
|
if ((usb_tmp = usb_head)) |
|
|
|
|
do { |
|
|
|
|
if (bus_number == usb_tmp->bus_number |
|
|
|
|
&& device_address == usb_tmp->device_address) { |
|
|
|
|
used = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
usb_tmp = usb_tmp->next; |
|
|
|
|
|
|
|
|
|
} while (usb_tmp != usb_head); |
|
|
|
|
|
|
|
|
|
if (lock) |
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
|
|
|
|
|
return used; |
|
|
|
|
} |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
#else |
|
|
|
|
#include <errno.h> |
|
|
|
|
#include <unistd.h> |
|
|
|
|
#include <sys/types.h> |
|
|
|
|
#include <sys/ipc.h> |
|
|
|
|
#include <sys/sem.h> |
|
|
|
|
#include <sys/types.h> |
|
|
|
|
#include <sys/stat.h> |
|
|
|
|
#include <fcntl.h> |
|
|
|
|
|
|
|
|
|
union semun { |
|
|
|
|
int sem; |
|
|
|
|
struct semid_ds *seminfo; |
|
|
|
|
ushort *all; |
|
|
|
|
}; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static void add_used(libusb_device *dev, bool lock) |
|
|
|
|
// 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) |
|
|
|
|
{ |
|
|
|
|
struct usb_list *usb_tmp; |
|
|
|
|
char buf[128]; |
|
|
|
|
uint8_t bus_number; |
|
|
|
|
uint8_t device_address; |
|
|
|
|
|
|
|
|
|
bus_number = libusb_get_bus_number(dev); |
|
|
|
|
device_address = libusb_get_device_address(dev); |
|
|
|
|
|
|
|
|
|
if (lock) |
|
|
|
|
mutex_lock(list_lock); |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
#else |
|
|
|
|
struct semid_ds seminfo; |
|
|
|
|
union semun opt; |
|
|
|
|
char name[64]; |
|
|
|
|
key_t key; |
|
|
|
|
int fd, sem, count; |
|
|
|
|
|
|
|
|
|
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 (in_use(dev, false)) { |
|
|
|
|
if (lock) |
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sprintf(buf, "add_used() duplicate bus_number %d device_address %d", |
|
|
|
|
(int)bus_number, (int)device_address); |
|
|
|
|
quit(1, buf); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
if (opt.seminfo->sem_otime != 0) |
|
|
|
|
break; |
|
|
|
|
nmsleep(1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
usb_tmp = malloc(sizeof(*usb_tmp)); |
|
|
|
|
|
|
|
|
|
usb_tmp->bus_number = bus_number; |
|
|
|
|
usb_tmp->device_address = device_address; |
|
|
|
|
|
|
|
|
|
if (usb_head) { |
|
|
|
|
// add to end
|
|
|
|
|
usb_tmp->prev = usb_head->prev; |
|
|
|
|
usb_tmp->next = usb_head; |
|
|
|
|
usb_head->prev = usb_tmp; |
|
|
|
|
usb_tmp->prev->next = usb_tmp; |
|
|
|
|
} else { |
|
|
|
|
usb_tmp->prev = usb_tmp; |
|
|
|
|
usb_tmp->next = usb_tmp; |
|
|
|
|
usb_head = usb_tmp; |
|
|
|
|
struct sembuf sops[] = { |
|
|
|
|
{ 0, 0, IPC_NOWAIT | SEM_UNDO }, |
|
|
|
|
{ 0, 1, IPC_NOWAIT | SEM_UNDO } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if (semop(sem, sops, 2)) { |
|
|
|
|
if (errno == EAGAIN) { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (lock) |
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
return true; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void release(uint8_t bus_number, uint8_t device_address, bool lock) |
|
|
|
|
static bool cgminer_usb_lock(struct device_drv *drv, libusb_device *dev) |
|
|
|
|
{ |
|
|
|
|
struct usb_list *usb_tmp; |
|
|
|
|
bool found = false; |
|
|
|
|
char buf[128]; |
|
|
|
|
|
|
|
|
|
if (lock) |
|
|
|
|
mutex_lock(list_lock); |
|
|
|
|
|
|
|
|
|
if ((usb_tmp = usb_head)) |
|
|
|
|
do { |
|
|
|
|
if (bus_number == usb_tmp->bus_number |
|
|
|
|
&& device_address == usb_tmp->device_address) { |
|
|
|
|
found = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
usb_tmp = usb_tmp->next; |
|
|
|
|
return cgminer_usb_lock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} while (usb_tmp != usb_head); |
|
|
|
|
// 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 |
|
|
|
|
#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", |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!found) { |
|
|
|
|
if (lock) |
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
struct sembuf sops[] = { |
|
|
|
|
{ 0, -1, SEM_UNDO } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
sprintf(buf, "release() unknown: bus_number %d device_address %d", |
|
|
|
|
(int)bus_number, (int)device_address); |
|
|
|
|
quit(1, buf); |
|
|
|
|
} |
|
|
|
|
// Allow a 10ms timeout
|
|
|
|
|
// exceeding this timeout means it would probably never succeed anyway
|
|
|
|
|
struct timespec timeout = { 0, 10000000 }; |
|
|
|
|
|
|
|
|
|
if (usb_tmp->next == usb_tmp) { |
|
|
|
|
usb_head = NULL; |
|
|
|
|
} else { |
|
|
|
|
if (usb_head == usb_tmp) |
|
|
|
|
usb_head = usb_tmp->next; |
|
|
|
|
usb_tmp->next->prev = usb_tmp->prev; |
|
|
|
|
usb_tmp->prev->next = usb_tmp->next; |
|
|
|
|
// Wait forever since we shoud be the one who has it
|
|
|
|
|
if (semtimedop(sem, sops, 1, &timeout)) { |
|
|
|
|
applog(LOG_ERR, |
|
|
|
|
"SEM: %d USB failed to release '%s' err (%d) %s", |
|
|
|
|
drv->dname, name, errno, strerror(errno)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (lock) |
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
|
|
|
|
|
free(usb_tmp); |
|
|
|
|
return; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void release_dev(libusb_device *dev, bool lock) |
|
|
|
|
static void cgminer_usb_unlock(struct device_drv *drv, libusb_device *dev) |
|
|
|
|
{ |
|
|
|
|
uint8_t bus_number; |
|
|
|
|
uint8_t device_address; |
|
|
|
|
|
|
|
|
|
bus_number = libusb_get_bus_number(dev); |
|
|
|
|
device_address = libusb_get_device_address(dev); |
|
|
|
|
|
|
|
|
|
release(bus_number, device_address, lock); |
|
|
|
|
cgminer_usb_unlock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) |
|
|
|
@ -771,7 +783,7 @@ void release_cgpu(struct cgpu_info *cgpu)
@@ -771,7 +783,7 @@ void release_cgpu(struct cgpu_info *cgpu)
|
|
|
|
|
|
|
|
|
|
usb_uninit(cgpu); |
|
|
|
|
|
|
|
|
|
release(cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address, true); |
|
|
|
|
cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found) |
|
|
|
@ -1053,21 +1065,11 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi
@@ -1053,21 +1065,11 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi
|
|
|
|
|
applog(LOG_DEBUG, "USB scan devices: found no devices"); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) { |
|
|
|
|
mutex_lock(list_lock); |
|
|
|
|
|
|
|
|
|
if (in_use(list[i], false)) |
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
else { |
|
|
|
|
add_used(list[i], false); |
|
|
|
|
|
|
|
|
|
mutex_unlock(list_lock); |
|
|
|
|
|
|
|
|
|
found = usb_check(drv, list[i]); |
|
|
|
|
if (!found) |
|
|
|
|
release_dev(list[i], true); |
|
|
|
|
else |
|
|
|
|
found = usb_check(drv, list[i]); |
|
|
|
|
if (found) { |
|
|
|
|
if (cgminer_usb_lock(drv, list[i]) == true) |
|
|
|
|
if (!device_detect(list[i], found)) |
|
|
|
|
release_dev(list[i], true); |
|
|
|
|
cgminer_usb_unlock(drv, list[i]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|