LedDeviceHyperionUsbasp.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // stl includes
  2. #include <exception>
  3. #include <cstring>
  4. // Local Hyperion includes
  5. #include "LedDeviceHyperionUsbasp.h"
  6. // Constants which define the Hyperion USBasp device
  7. namespace {
  8. uint16_t _usbVendorId = 0x16c0;
  9. uint16_t _usbProductId = 0x05dc;
  10. QString _usbProductDescription = "Hyperion led controller";
  11. }
  12. LedDeviceHyperionUsbasp::LedDeviceHyperionUsbasp(const QJsonObject &deviceConfig)
  13. : LedDevice(deviceConfig)
  14. , _libusbContext(nullptr)
  15. , _deviceHandle(nullptr)
  16. {
  17. }
  18. LedDeviceHyperionUsbasp::~LedDeviceHyperionUsbasp()
  19. {
  20. if (_libusbContext != nullptr)
  21. {
  22. libusb_exit(_libusbContext);
  23. }
  24. }
  25. LedDevice* LedDeviceHyperionUsbasp::construct(const QJsonObject &deviceConfig)
  26. {
  27. return new LedDeviceHyperionUsbasp(deviceConfig);
  28. }
  29. bool LedDeviceHyperionUsbasp::init(const QJsonObject &deviceConfig)
  30. {
  31. bool isInitOK = false;
  32. // Initialise sub-class
  33. if ( LedDevice::init(deviceConfig) )
  34. {
  35. QString ledType = deviceConfig["ledType"].toString("ws2801");
  36. if (ledType != "ws2801" && ledType != "ws2812")
  37. {
  38. QString errortext = QString ("Invalid ledType; must be 'ws2801' or 'ws2812'.");
  39. this->setInError(errortext);
  40. isInitOK = false;
  41. }
  42. else
  43. {
  44. _writeLedsCommand = (ledType == "ws2801") ? CMD_WRITE_WS2801 : CMD_WRITE_WS2812;
  45. int error;
  46. // initialize the USB context
  47. if ( (error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS )
  48. {
  49. _libusbContext = nullptr;
  50. QString errortext = QString ("Error while initializing USB context(%1):%2").arg(error).arg(libusb_error_name(error));
  51. this->setInError(errortext);
  52. isInitOK = false;
  53. }
  54. else
  55. {
  56. Debug(_log, "USB context initialized");
  57. #if 0
  58. libusb_set_debug(_libusbContext, 3);
  59. #endif
  60. // retrieve the list of USB devices
  61. libusb_device ** deviceList;
  62. ssize_t deviceCount = libusb_get_device_list(_libusbContext, &deviceList);
  63. // iterate the list of devices
  64. for (ssize_t i = 0 ; i < deviceCount; ++i)
  65. {
  66. // try to open and initialize the device
  67. if ( testAndOpen(deviceList[i]) == 0 )
  68. {
  69. _device = deviceList[i];
  70. // a device was successfully opened. break from list
  71. break;
  72. }
  73. }
  74. // free the device list
  75. libusb_free_device_list(deviceList, 1);
  76. if (_deviceHandle == nullptr)
  77. {
  78. QString errortext;
  79. errortext = QString ("No %1 has been found").arg( _usbProductDescription);
  80. this->setInError( errortext );
  81. }
  82. else
  83. {
  84. isInitOK = true;
  85. }
  86. }
  87. }
  88. }
  89. return isInitOK;
  90. }
  91. int LedDeviceHyperionUsbasp::open()
  92. {
  93. int retval = -1;
  94. _isDeviceReady = false;
  95. if ( libusb_open(_device, &_deviceHandle) != LIBUSB_SUCCESS )
  96. {
  97. QString errortext = QString ("Failed to open [%1]").arg(_usbProductDescription);
  98. this->setInError(errortext);
  99. }
  100. else
  101. {
  102. // Everything is OK -> enable device
  103. _isDeviceReady = true;
  104. retval = 0;
  105. }
  106. return retval;
  107. }
  108. int LedDeviceHyperionUsbasp::close()
  109. {
  110. int retval = 0;
  111. _isDeviceReady = false;
  112. // LedDevice specific closing activities
  113. if (_deviceHandle != nullptr)
  114. {
  115. libusb_release_interface(_deviceHandle, 0);
  116. libusb_attach_kernel_driver(_deviceHandle, 0);
  117. libusb_close(_deviceHandle);
  118. _deviceHandle = nullptr;
  119. }
  120. return retval;
  121. }
  122. int LedDeviceHyperionUsbasp::testAndOpen(libusb_device * device)
  123. {
  124. libusb_device_descriptor deviceDescriptor;
  125. int error = libusb_get_device_descriptor(device, &deviceDescriptor);
  126. if (error != LIBUSB_SUCCESS)
  127. {
  128. Error(_log, "Error while retrieving device descriptor(%d): %s", error, libusb_error_name(error));
  129. return -1;
  130. }
  131. if (deviceDescriptor.idVendor == _usbVendorId &&
  132. deviceDescriptor.idProduct == _usbProductId &&
  133. deviceDescriptor.iProduct != 0 &&
  134. getString(device, deviceDescriptor.iProduct) == _usbProductDescription)
  135. {
  136. // get the hardware address
  137. int busNumber = libusb_get_bus_number(device);
  138. int addressNumber = libusb_get_device_address(device);
  139. Info(_log, "%s found: bus=%d address=%d", QSTRING_CSTR(_usbProductDescription), busNumber, addressNumber);
  140. // TODO: Check, if exceptions via try/catch need to be replaced in Qt environment
  141. try
  142. {
  143. _deviceHandle = openDevice(device);
  144. Info(_log, "%s successfully opened", QSTRING_CSTR(_usbProductDescription) );
  145. return 0;
  146. }
  147. catch(int e)
  148. {
  149. _deviceHandle = nullptr;
  150. Error(_log, "Unable to open %s. Searching for other device(%d): %s", QSTRING_CSTR(_usbProductDescription), e, libusb_error_name(e));
  151. }
  152. }
  153. return -1;
  154. }
  155. int LedDeviceHyperionUsbasp::write(const std::vector<ColorRgb> &ledValues)
  156. {
  157. int nbytes = libusb_control_transfer(
  158. _deviceHandle, // device handle
  159. static_cast<uint8_t>( LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT ), // request type
  160. _writeLedsCommand, // request
  161. 0, // value
  162. 0, // index
  163. (uint8_t *) ledValues.data(), // data
  164. (3*_ledCount) & 0xffff, // length
  165. 5000); // timeout
  166. // Disabling interrupts for a little while on the device results in a PIPE error. All seems to keep functioning though...
  167. if(nbytes < 0 && nbytes != LIBUSB_ERROR_PIPE)
  168. {
  169. Error(_log, "Error while writing data to %s (%s)", QSTRING_CSTR(_usbProductDescription), libusb_error_name(nbytes));
  170. return -1;
  171. }
  172. return 0;
  173. }
  174. libusb_device_handle * LedDeviceHyperionUsbasp::openDevice(libusb_device *device)
  175. {
  176. Logger * log = Logger::getInstance("LedDevice");
  177. libusb_device_handle * handle = nullptr;
  178. int error = libusb_open(device, &handle);
  179. if (error != LIBUSB_SUCCESS)
  180. {
  181. Error(log, "unable to open device(%d): %s",error,libusb_error_name(error));
  182. throw error;
  183. }
  184. // detach kernel driver if it is active
  185. if (libusb_kernel_driver_active(handle, 0) == 1)
  186. {
  187. error = libusb_detach_kernel_driver(handle, 0);
  188. if (error != LIBUSB_SUCCESS)
  189. {
  190. Error(log, "unable to detach kernel driver(%d): %s",error,libusb_error_name(error));
  191. libusb_close(handle);
  192. throw error;
  193. }
  194. }
  195. error = libusb_claim_interface(handle, 0);
  196. if (error != LIBUSB_SUCCESS)
  197. {
  198. Error(log, "unable to claim interface(%d): %s", error, libusb_error_name(error));
  199. libusb_attach_kernel_driver(handle, 0);
  200. libusb_close(handle);
  201. throw error;
  202. }
  203. return handle;
  204. }
  205. QString LedDeviceHyperionUsbasp::getString(libusb_device * device, int stringDescriptorIndex)
  206. {
  207. libusb_device_handle * handle = nullptr;
  208. int error = libusb_open(device, &handle);
  209. if (error != LIBUSB_SUCCESS)
  210. {
  211. throw error;
  212. }
  213. char buffer[256];
  214. error = libusb_get_string_descriptor_ascii(handle, stringDescriptorIndex, reinterpret_cast<unsigned char *>(buffer), sizeof(buffer));
  215. if (error <= 0)
  216. {
  217. libusb_close(handle);
  218. throw error;
  219. }
  220. libusb_close(handle);
  221. return QString(QByteArray(buffer, error));
  222. }