SCSI2SD_HID.cc 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
  2. //
  3. // This file is part of SCSI2SD.
  4. //
  5. // SCSI2SD is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // SCSI2SD is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
  17. #include "SCSI2SD_HID.hh"
  18. #include "scsi2sd.h"
  19. #include "hidpacket.h"
  20. // For compilers that support precompilation, includes "wx/wx.h".
  21. #include <wx/wxprec.h>
  22. #ifndef WX_PRECOMP
  23. #include <wx/wx.h>
  24. #endif
  25. #include <wx/utils.h>
  26. #include <cassert>
  27. #include <stdexcept>
  28. #include <sstream>
  29. #include <iostream>
  30. #include <string.h> // memcpy
  31. using namespace SCSI2SD;
  32. HID::HID(hid_device_info* hidInfo) :
  33. myHidInfo(hidInfo),
  34. myConfigHandle(NULL),
  35. myDebugHandle(NULL),
  36. myFirmwareVersion(0),
  37. mySDCapacity(0)
  38. {
  39. // hidInfo->interface_number not supported on mac, and interfaces
  40. // are enumerated in a random order. :-(
  41. // We rely on the watchdog value of the debug interface changing on each
  42. // read to differentiate the interfaces.
  43. try
  44. {
  45. while (hidInfo && !(myConfigHandle && myDebugHandle))
  46. {
  47. std::stringstream msg;
  48. msg << "Error opening HID device " << hidInfo->path << std::endl;
  49. if ((hidInfo->interface_number == CONFIG_INTERFACE) ||
  50. (hidInfo->usage_page == 0xFF00))
  51. {
  52. myConfigHandle = hid_open_path(hidInfo->path);
  53. if (!myConfigHandle) throw std::runtime_error(msg.str());
  54. hidInfo = hidInfo->next;
  55. }
  56. else if ((hidInfo->interface_number == DEBUG_INTERFACE) ||
  57. (hidInfo->usage_page == 0xFF01))
  58. {
  59. myDebugHandle = hid_open_path(hidInfo->path);
  60. if (!myDebugHandle) throw std::runtime_error(msg.str());
  61. readDebugData();
  62. hidInfo = hidInfo->next;
  63. }
  64. else if (hidInfo->interface_number == -1)
  65. {
  66. // hidInfo->interface_number not supported on mac, and
  67. // interfaces are enumerated in a random order. :-(
  68. // We rely on the watchdog value of the debug interface
  69. // changing on each read to differentiate the interfaces.
  70. // Not necessary since firmware 3.5.2 as the usage_page can
  71. // be used instead.
  72. hid_device* dev = hid_open_path(hidInfo->path);
  73. if (!dev)
  74. {
  75. throw std::runtime_error(msg.str());
  76. }
  77. uint8_t buf[HID_PACKET_SIZE];
  78. int watchVal = -1;
  79. int configIntFound = 1;
  80. for (int i = 0; i < 4; ++i)
  81. {
  82. buf[0] = 0; // report id
  83. hid_read_timeout(dev, buf, HID_PACKET_SIZE, HID_TIMEOUT_MS);
  84. if (watchVal == -1) watchVal = buf[25];
  85. configIntFound = configIntFound && (buf[25] == watchVal);
  86. }
  87. if (configIntFound)
  88. {
  89. myConfigHandle = dev;
  90. }
  91. else
  92. {
  93. myDebugHandle = dev;
  94. readDebugData();
  95. }
  96. }
  97. }
  98. }
  99. catch (std::runtime_error& e)
  100. {
  101. destroy();
  102. throw e;
  103. }
  104. }
  105. void
  106. HID::destroy()
  107. {
  108. if (myConfigHandle)
  109. {
  110. hid_close(myConfigHandle);
  111. myConfigHandle = NULL;
  112. }
  113. if (myDebugHandle)
  114. {
  115. hid_close(myDebugHandle);
  116. myDebugHandle = NULL;
  117. }
  118. hid_free_enumeration(myHidInfo);
  119. myHidInfo = NULL;
  120. }
  121. HID::~HID()
  122. {
  123. destroy();
  124. }
  125. HID*
  126. HID::Open()
  127. {
  128. hid_device_info* dev = hid_enumerate(VENDOR_ID, PRODUCT_ID);
  129. if (dev)
  130. {
  131. return new HID(dev);
  132. }
  133. else
  134. {
  135. return NULL;
  136. }
  137. }
  138. void
  139. HID::enterBootloader()
  140. {
  141. // Reboot commands added in firmware 3.5
  142. if (!myDebugHandle)
  143. {
  144. throw std::runtime_error(
  145. "Cannot enter SCSI2SD bootloader: debug interface not found");
  146. }
  147. else if (myFirmwareVersion == 0)
  148. {
  149. throw std::runtime_error(
  150. "Cannot enter SCSI2SD bootloader: old firmware version running.\n"
  151. "The SCSI2SD board cannot reset itself. Please disconnect and \n"
  152. "reconnect the USB cable.\n");
  153. }
  154. else
  155. {
  156. uint8_t hidBuf[HID_PACKET_SIZE + 1] =
  157. {
  158. 0x00, // Report ID;
  159. 0x01 // Reboot command
  160. // 63 bytes unused.
  161. };
  162. int result = hid_write(myDebugHandle, hidBuf, sizeof(hidBuf));
  163. if (result <= 0)
  164. {
  165. const wchar_t* err = hid_error(myDebugHandle);
  166. std::stringstream ss;
  167. ss << "USB HID write failure: " << err;
  168. throw std::runtime_error(ss.str());
  169. }
  170. }
  171. }
  172. void
  173. HID::readFlashRow(int array, int row, std::vector<uint8_t>& out)
  174. {
  175. std::vector<uint8_t> cmd
  176. {
  177. CONFIG_READFLASH,
  178. static_cast<uint8_t>(array),
  179. static_cast<uint8_t>(row)
  180. };
  181. sendHIDPacket(cmd, out, HIDPACKET_MAX_LEN / 62);
  182. }
  183. void
  184. HID::writeFlashRow(int array, int row, const std::vector<uint8_t>& in)
  185. {
  186. std::vector<uint8_t> cmd;
  187. cmd.push_back(CONFIG_WRITEFLASH);
  188. cmd.insert(cmd.end(), in.begin(), in.end());
  189. cmd.push_back(static_cast<uint8_t>(array));
  190. cmd.push_back(static_cast<uint8_t>(row));
  191. std::vector<uint8_t> out;
  192. sendHIDPacket(cmd, out, 1);
  193. if ((out.size() < 1) || (out[0] != CONFIG_STATUS_GOOD))
  194. {
  195. std::stringstream ss;
  196. ss << "Error writing flash " << array << "/" << row;
  197. throw std::runtime_error(ss.str());
  198. }
  199. }
  200. bool
  201. HID::readSCSIDebugInfo(std::vector<uint8_t>& buf)
  202. {
  203. buf[0] = 0; // report id
  204. hid_set_nonblocking(myDebugHandle, 1);
  205. int result =
  206. hid_read_timeout(
  207. myDebugHandle,
  208. &buf[0],
  209. HID_PACKET_SIZE,
  210. HID_TIMEOUT_MS);
  211. hid_set_nonblocking(myDebugHandle, 0);
  212. if (result <= 0)
  213. {
  214. const wchar_t* err = hid_error(myDebugHandle);
  215. std::stringstream ss;
  216. ss << "USB HID read failure: " << err;
  217. throw std::runtime_error(ss.str());
  218. }
  219. return result > 0;
  220. }
  221. void
  222. HID::readHID(uint8_t* buffer, size_t len)
  223. {
  224. assert(len >= 0);
  225. buffer[0] = 0; // report id
  226. int result = -1;
  227. for (int retry = 0; retry < 3 && result <= 0; ++retry)
  228. {
  229. result = hid_read_timeout(myConfigHandle, buffer, len, HID_TIMEOUT_MS);
  230. }
  231. if (result < 0)
  232. {
  233. const wchar_t* err = hid_error(myConfigHandle);
  234. std::stringstream ss;
  235. ss << "USB HID read failure: " << err;
  236. throw std::runtime_error(ss.str());
  237. }
  238. }
  239. void
  240. HID::readDebugData()
  241. {
  242. uint8_t buf[HID_PACKET_SIZE];
  243. buf[0] = 0; // report id
  244. int result =
  245. hid_read_timeout(
  246. myDebugHandle,
  247. buf,
  248. HID_PACKET_SIZE,
  249. HID_TIMEOUT_MS);
  250. if (result <= 0)
  251. {
  252. const wchar_t* err = hid_error(myDebugHandle);
  253. std::stringstream ss;
  254. ss << "USB HID read failure: " << err;
  255. throw std::runtime_error(ss.str());
  256. }
  257. myFirmwareVersion = (((uint16_t)buf[62]) << 8) | buf[63];
  258. mySDCapacity =
  259. (((uint32_t)buf[58]) << 24) |
  260. (((uint32_t)buf[59]) << 16) |
  261. (((uint32_t)buf[60]) << 8) |
  262. ((uint32_t)buf[61]);
  263. }
  264. std::string
  265. HID::getFirmwareVersionStr() const
  266. {
  267. if (myFirmwareVersion == 0)
  268. {
  269. return "Unknown (3.0 - 3.4)";
  270. }
  271. else
  272. {
  273. std::stringstream ver;
  274. ver << std::hex <<
  275. (myFirmwareVersion >> 8) <<
  276. '.' << ((myFirmwareVersion & 0xF0) >> 4);
  277. int rev = myFirmwareVersion & 0xF;
  278. if (rev)
  279. {
  280. ver << "." << rev;
  281. }
  282. return ver.str();
  283. }
  284. }
  285. bool
  286. HID::ping()
  287. {
  288. std::vector<uint8_t> cmd { CONFIG_PING };
  289. std::vector<uint8_t> out;
  290. try
  291. {
  292. sendHIDPacket(cmd, out, 1);
  293. }
  294. catch (std::runtime_error& e)
  295. {
  296. return false;
  297. }
  298. return (out.size() >= 1) && (out[0] == CONFIG_STATUS_GOOD);
  299. }
  300. std::vector<uint8_t>
  301. HID::getSD_CSD()
  302. {
  303. std::vector<uint8_t> cmd { CONFIG_SDINFO };
  304. std::vector<uint8_t> out;
  305. try
  306. {
  307. sendHIDPacket(cmd, out, 16);
  308. }
  309. catch (std::runtime_error& e)
  310. {
  311. return std::vector<uint8_t>(16);
  312. }
  313. out.resize(16);
  314. return out;
  315. }
  316. std::vector<uint8_t>
  317. HID::getSD_CID()
  318. {
  319. std::vector<uint8_t> cmd { CONFIG_SDINFO };
  320. std::vector<uint8_t> out;
  321. try
  322. {
  323. sendHIDPacket(cmd, out, 16);
  324. }
  325. catch (std::runtime_error& e)
  326. {
  327. return std::vector<uint8_t>(16);
  328. }
  329. std::vector<uint8_t> result(16);
  330. for (size_t i = 0; i < 16; ++i) result[i] = out[16 + i];
  331. return result;
  332. }
  333. bool
  334. HID::scsiSelfTest()
  335. {
  336. std::vector<uint8_t> cmd { CONFIG_SCSITEST };
  337. std::vector<uint8_t> out;
  338. try
  339. {
  340. sendHIDPacket(cmd, out, 2);
  341. }
  342. catch (std::runtime_error& e)
  343. {
  344. return false;
  345. }
  346. return (out.size() >= 1) && (out[0] == CONFIG_STATUS_GOOD);
  347. }
  348. void
  349. HID::sendHIDPacket(
  350. const std::vector<uint8_t>& cmd,
  351. std::vector<uint8_t>& out,
  352. size_t responseLength)
  353. {
  354. assert(cmd.size() <= HIDPACKET_MAX_LEN);
  355. hidPacket_send(&cmd[0], cmd.size());
  356. uint8_t hidBuf[HID_PACKET_SIZE];
  357. const uint8_t* chunk = hidPacket_getHIDBytes(hidBuf);
  358. while (chunk)
  359. {
  360. uint8_t reportBuf[HID_PACKET_SIZE + 1] = { 0x00 }; // Report ID
  361. memcpy(&reportBuf[1], chunk, HID_PACKET_SIZE);
  362. int result = -1;
  363. for (int retry = 0; retry < 10 && result <= 0; ++retry)
  364. {
  365. result = hid_write(myConfigHandle, reportBuf, sizeof(reportBuf));
  366. }
  367. if (result <= 0)
  368. {
  369. const wchar_t* err = hid_error(myConfigHandle);
  370. std::stringstream ss;
  371. ss << "USB HID write failure: " << err;
  372. throw std::runtime_error(ss.str());
  373. }
  374. chunk = hidPacket_getHIDBytes(hidBuf);
  375. }
  376. const uint8_t* resp = NULL;
  377. size_t respLen;
  378. resp = hidPacket_getPacket(&respLen);
  379. for (unsigned int retry = 0; retry < responseLength * 2 && !resp; ++retry)
  380. {
  381. readHID(hidBuf, sizeof(hidBuf)); // Will block
  382. hidPacket_recv(hidBuf, HID_PACKET_SIZE);
  383. resp = hidPacket_getPacket(&respLen);
  384. }
  385. if (!resp)
  386. {
  387. throw std::runtime_error("SCSI2SD config protocol error");
  388. }
  389. out.insert(
  390. out.end(),
  391. resp,
  392. resp + respLen);
  393. }