hanvon-libusb.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /*
  2. * =====================================================================================
  3. *
  4. * Filename: hanvon-libusb.c
  5. *
  6. * Description: libusb Hanvon tablet driver
  7. *
  8. * Version: 0.1
  9. * Created: 08/17/2020 04:05:14 PM
  10. * Revision: none
  11. * Compiler: gcc
  12. *
  13. * Maintained by: scuti@teknik.io
  14. * surkeh@protonmail.com
  15. *
  16. * =====================================================================================
  17. */
  18. #define DEBUG(msg,...) fprintf(stderr,"%s(%d): " msg , __FILE__,__LINE__,__VA_ARGS__)
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <libusb-1.0/libusb.h>
  22. #include <libevdev/libevdev.h>
  23. #include <libevdev/libevdev-uinput.h>
  24. #define STATE_SUCCESS 0
  25. #define STATE_NOT_FOUND 1
  26. #define VENDOR_ID_HANVON 0x0b57
  27. #define PRODUCT_ID_AM3M 0x8528
  28. #define PRODUCT_ID_AM0806 0x8502
  29. #define PRODUCT_ID_AM0605 0x8503
  30. #define PRODUCT_ID_AM1107 0x8505
  31. #define PRODUCT_ID_AM1209 0x8501
  32. #define PRODUCT_ID_RL0604 0x851f
  33. #define PRODUCT_ID_RL0504 0x851d
  34. #define PRODUCT_ID_GP0806 0x8039
  35. #define PRODUCT_ID_GP0806B 0x8511
  36. #define PRODUCT_ID_GP0605 0x8512
  37. #define PRODUCT_ID_GP0605A 0x803a
  38. #define PRODUCT_ID_GP0504 0x8037
  39. #define PRODUCT_ID_NXS1513 0x8030
  40. #define PRODUCT_ID_GP0906 0x8521
  41. #define PRODUCT_ID_APPIV0906 0x8532
  42. #define AM_PACKET_LEN 10
  43. #define AM_RESOLUTION 40
  44. #define AM_WHEEL_THRESHOLD 4
  45. #define AM_MAX_ABS_X 0x27DE
  46. #define AM_MAX_ABS_Y 0x1CFE
  47. #define AM_MAX_TILT_X 0x3F
  48. #define AM_MAX_TILT_Y 0x7F
  49. #define AM_MAX_PRESSURE 0x400
  50. #define APPIV_MAX_ABS_X 0x5750
  51. #define APPIV_MAX_ABS_Y 0x5750
  52. #define BUTTON_EVENT_GP 0x01
  53. #define PEN_EVENT 0x02
  54. #define BUTTON_EVENT_0906 0x0C
  55. static int lbuttons[]={BTN_0,BTN_1,BTN_2,BTN_3}; /* reported on all AMs */
  56. static int rbuttons[]={BTN_4,BTN_5,BTN_6,BTN_7}; /* reported on AM1107+ */
  57. struct hanvon_message {
  58. unsigned char msgtype;
  59. unsigned char is_move;
  60. unsigned short x_movement;
  61. unsigned short y_movement;
  62. unsigned char pressure;
  63. unsigned char x_tilt;
  64. unsigned char y_tilt;
  65. };
  66. // GLOBAL
  67. int wheel_position;
  68. int find_device(libusb_device **list, unsigned int count) {
  69. if (count < 0) {
  70. return -1;
  71. }
  72. int found = -1;
  73. struct libusb_device_descriptor desc;
  74. for (unsigned int i = 0; i < count; i++) {
  75. libusb_device *t = list[i];
  76. libusb_get_device_descriptor(list[i], &desc);
  77. if (desc.idVendor == VENDOR_ID_HANVON) {
  78. switch(desc.idProduct) {
  79. default:
  80. break;
  81. case PRODUCT_ID_AM0806:
  82. case PRODUCT_ID_AM0605:
  83. case PRODUCT_ID_AM1107:
  84. case PRODUCT_ID_AM1209:
  85. case PRODUCT_ID_RL0604:
  86. case PRODUCT_ID_RL0504:
  87. case PRODUCT_ID_GP0806:
  88. case PRODUCT_ID_GP0806B:
  89. case PRODUCT_ID_GP0605:
  90. case PRODUCT_ID_GP0605A:
  91. case PRODUCT_ID_GP0504:
  92. case PRODUCT_ID_NXS1513:
  93. case PRODUCT_ID_GP0906:
  94. case PRODUCT_ID_APPIV0906:
  95. return i;
  96. } // end switch
  97. } // end if
  98. } // end for
  99. return found;
  100. }
  101. void display_packets(const unsigned char* buf) {
  102. for(int i = 0; i < AM_PACKET_LEN; i++) {
  103. fprintf(stderr,"0x%x, ", buf[i]);
  104. }
  105. fprintf(stderr,"\r");
  106. }
  107. void callback(struct libusb_transfer *transfer) {
  108. unsigned char *data = transfer -> buffer;
  109. display_packets(data);
  110. }
  111. static inline void report_buttons( struct libevdev_uinput *ud,
  112. int buttons[],
  113. unsigned char data)
  114. {
  115. int err = 0;
  116. if((data & 0xf0) == 0xa0) {
  117. // TODO test that these are the correct buttons and all buttons are covered
  118. err = libevdev_uinput_write_event(ud, EV_KEY, buttons[1], (data & 0x02));
  119. if(err) { DEBUG("err: %d\n",err); }
  120. err = libevdev_uinput_write_event(ud, EV_KEY, buttons[2], (data & 0x04));
  121. if(err) { DEBUG("err: %d\n",err); }
  122. err = libevdev_uinput_write_event(ud, EV_KEY, buttons[3], (data & 0x08));
  123. if(err) { DEBUG("err: %d\n",err); }
  124. } else if(data <= 0x3f) { /* slider area active */
  125. int delta = data - wheel_position;
  126. if(abs(delta) < AM_WHEEL_THRESHOLD) {
  127. err = libevdev_uinput_write_event(ud, EV_REL, REL_WHEEL, delta); // TODO test delta as input
  128. if(err) { DEBUG("err: %d\n",err); }
  129. wheel_position = data;
  130. }
  131. }
  132. }
  133. // NOTE:
  134. // Judging by the original driver, this should work for all but may not work
  135. // for the APPIV0906. Possibly needs little endian for APPIV0906 x and y data
  136. // but we don't have any means of testing this without that tablet.
  137. // NOTE:
  138. // Left and right mouse click should work for all but additional buttons are
  139. // not supported by the default handler.
  140. void callback_default (struct libusb_transfer *tx) { // for callback
  141. unsigned char *data = tx -> buffer;
  142. struct hanvon_message *msg = (struct hanvon_message *)tx -> buffer;
  143. int err = 0;
  144. struct libevdev_uinput *ud = tx -> user_data;
  145. switch(msg->msgtype) {
  146. case BUTTON_EVENT_GP:
  147. if(data[1] == 0x55) { // left side buttons
  148. report_buttons(ud, lbuttons, msg->x_movement); // button pressed data in same place as position data
  149. }
  150. if(data[3] == 0xAA) { // right side buttons (am1107, am1209
  151. report_buttons(ud, rbuttons, msg->y_movement); // button pressed data in same place as position data
  152. }
  153. break;
  154. case PEN_EVENT:
  155. /* is_move values:
  156. 0x80: near, 0x02: button press
  157. 0x10: floating, 0x01: touching */
  158. err = libevdev_uinput_write_event(
  159. ud, EV_KEY, BTN_TOOL_PEN, msg->is_move & (0x80|0x10)
  160. );
  161. if(err) { DEBUG("err: %d\n",err); }
  162. if(msg->is_move & (0x80|0x10)) {
  163. msg->x_movement = htobe16(msg->x_movement);
  164. //DEBUG("Set X to %x\n",msg->x_movement);
  165. err = libevdev_uinput_write_event(
  166. ud, EV_ABS, ABS_X, msg->x_movement
  167. );
  168. if(err) { DEBUG("err: %d\n",err); }
  169. msg->y_movement = htobe16(msg->y_movement);
  170. //DEBUG("Set Y to %x\n",msg->y_movement);
  171. err = libevdev_uinput_write_event(
  172. ud, EV_ABS, ABS_Y, msg->y_movement
  173. );
  174. if(err) { DEBUG("err: %d\n",err); }
  175. }
  176. err = libevdev_uinput_write_event(
  177. ud, EV_ABS, ABS_PRESSURE, msg->pressure * 8 // reference original driver
  178. );
  179. if(err) { DEBUG("err: %d\n",err); }
  180. err = libevdev_uinput_write_event(
  181. ud, EV_ABS, ABS_TILT_X, msg->x_tilt
  182. );
  183. if(err) { DEBUG("err: %d\n",err); }
  184. err = libevdev_uinput_write_event(
  185. ud, EV_ABS, ABS_TILT_Y, msg->y_tilt
  186. );
  187. if(err) { DEBUG("err: %d\n",err); }
  188. err = libevdev_uinput_write_event(
  189. ud, EV_KEY, BTN_LEFT, msg->is_move & 0x01
  190. );
  191. if(err) { DEBUG("err: %d\n",err); }
  192. err = libevdev_uinput_write_event(
  193. ud, EV_KEY, BTN_RIGHT, (msg->is_move & 0x02) / 2
  194. );
  195. if(err) { DEBUG("err: %d\n",err); }
  196. // data[1]:
  197. // 0x10 = lift, 0x90 = close, 0x91 = press
  198. // 0x12 = btn (lift), 0x92 = btn (close), 0x93 = btn (press)
  199. break;
  200. case BUTTON_EVENT_0906:
  201. // TODO confirm this is the byte that contains button flags
  202. err = libevdev_uinput_write_event(
  203. ud, EV_KEY, BTN_0, (msg->is_move & 0x0100) / 2
  204. );
  205. if(err) { DEBUG("err: %d\n",err); }
  206. err = libevdev_uinput_write_event(
  207. ud, EV_KEY, BTN_1, (msg->is_move & 0x0200) / 2
  208. );
  209. if(err) { DEBUG("err: %d\n",err); }
  210. err = libevdev_uinput_write_event(
  211. ud, EV_KEY, BTN_2, (msg->is_move & 0x0400) / 2
  212. );
  213. if(err) { DEBUG("err: %d\n",err); }
  214. err = libevdev_uinput_write_event(
  215. ud, EV_KEY, BTN_3, (msg->is_move & 0x0800) / 2
  216. );
  217. if(err) { DEBUG("err: %d\n",err); }
  218. err = libevdev_uinput_write_event(
  219. ud, EV_KEY, BTN_4, (msg->is_move & 0x1000) / 2
  220. );
  221. if(err) { DEBUG("err: %d\n",err); }
  222. err = libevdev_uinput_write_event(
  223. ud, EV_KEY, BTN_5, (msg->is_move & 0x2000) / 2
  224. );
  225. if(err) { DEBUG("err: %d\n",err); }
  226. err = libevdev_uinput_write_event(
  227. ud, EV_KEY, BTN_6, (msg->is_move & 0x4000) / 2
  228. );
  229. if(err) { DEBUG("err: %d\n",err); }
  230. err = libevdev_uinput_write_event(
  231. ud, EV_KEY, BTN_7, (msg->is_move & 0x8000) / 2
  232. );
  233. if(err) { DEBUG("err: %d\n",err); }
  234. default:
  235. // do nothing
  236. break;
  237. }
  238. // always display packets
  239. display_packets(data);
  240. err += libevdev_uinput_write_event(ud, EV_SYN, SYN_REPORT, 0);
  241. if (err != 0) {
  242. printf("error : gp0504, %i\n", err);
  243. }
  244. return;
  245. }
  246. // https://www.freedesktop.org/software/libevdev/doc/latest/group__kernel.html
  247. int init_ctrl(struct libusb_device * const d,
  248. struct libevdev **evdev,
  249. struct libevdev_uinput **uidev) {
  250. struct input_absinfo *abs;
  251. printf("init_ctrl: %x\n", uidev);
  252. wheel_position = AM_WHEEL_THRESHOLD - 1; // init global
  253. if (d == NULL) {
  254. return -1;
  255. }
  256. int is_ok = 0;
  257. struct libusb_device_descriptor desc;
  258. libusb_get_device_descriptor(d, &desc);
  259. (*evdev) = libevdev_new();
  260. // set up inputs all devices have
  261. libevdev_enable_property((*evdev), INPUT_PROP_DIRECT);
  262. libevdev_enable_event_type((*evdev), EV_SYN);
  263. libevdev_enable_event_code((*evdev), EV_SYN, SYN_REPORT, NULL);
  264. libevdev_enable_event_type((*evdev), EV_KEY); // enable pen button
  265. libevdev_enable_event_code((*evdev), EV_KEY, BTN_TOOL_PEN, NULL);
  266. libevdev_enable_event_code((*evdev), EV_KEY, BTN_LEFT, NULL); // pen tap
  267. libevdev_enable_event_code((*evdev), EV_KEY, BTN_RIGHT, NULL); // pen button
  268. // enable absolute position, pressure, tilt
  269. libevdev_enable_event_type((*evdev), EV_ABS);
  270. abs = malloc(sizeof(struct input_absinfo));
  271. // set up absolute x coordinate input
  272. abs->value = 0x1000;
  273. abs->minimum = 0;
  274. switch(desc.idProduct) {
  275. case PRODUCT_ID_APPIV0906:
  276. abs->maximum = APPIV_MAX_ABS_X;
  277. break;
  278. default:
  279. abs->maximum = AM_MAX_ABS_X;
  280. break;
  281. }
  282. abs->fuzz = 0;
  283. abs->flat = 0;
  284. abs->resolution = AM_RESOLUTION;
  285. if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_X, abs)<0) {
  286. DEBUG("%s","failed to register absolute x\n");
  287. is_ok = -1;
  288. }
  289. // set up absolute y coordinate input
  290. abs->value = 0x1000;
  291. abs->minimum = 0;
  292. switch(desc.idProduct) {
  293. case PRODUCT_ID_APPIV0906:
  294. abs->maximum = APPIV_MAX_ABS_Y;
  295. break;
  296. default:
  297. abs->maximum = AM_MAX_ABS_Y;
  298. break;
  299. }
  300. abs->resolution = AM_RESOLUTION;
  301. if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_Y, abs)<0) {
  302. DEBUG("%s","failed to register absolute y\n");
  303. is_ok = -1;
  304. }
  305. // set up pressure input
  306. abs -> value = 0;
  307. abs -> minimum = 0;
  308. abs -> maximum = AM_MAX_PRESSURE;
  309. abs -> resolution = 0;
  310. if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_PRESSURE, abs)<0) {
  311. DEBUG("%s","failed to register pressure\n");
  312. is_ok = -1;
  313. }
  314. // set up tilt x input
  315. abs -> value = 0;
  316. abs -> minimum = 0;
  317. abs -> maximum = AM_MAX_TILT_X;
  318. abs -> resolution = 0;
  319. if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_TILT_X, abs)<0) {
  320. DEBUG("%s","failed to register x tilt\n");
  321. is_ok = -1;
  322. }
  323. // set up tilt y input
  324. abs -> value = 0;
  325. abs -> minimum = 0;
  326. abs -> maximum = AM_MAX_TILT_Y;
  327. abs -> resolution = 0;
  328. if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_TILT_Y, abs)<0) {
  329. DEBUG("%s","failed to register y tilt\n");
  330. is_ok = -1;
  331. }
  332. // Scroll wheel is NOT universal
  333. if(libevdev_enable_event_code((*evdev), EV_REL, REL_WHEEL, NULL)<0) {
  334. DEBUG("%s","failed to register scroll wheel\n");
  335. is_ok = -1;
  336. }
  337. // set up device-specific inputs
  338. switch(desc.idProduct) {
  339. case PRODUCT_ID_AM3M:
  340. case PRODUCT_ID_AM0806:
  341. case PRODUCT_ID_AM0605:
  342. libevdev_enable_event_code((*evdev), EV_KEY, BTN_0, NULL);
  343. libevdev_enable_event_code((*evdev), EV_KEY, BTN_1, NULL);
  344. libevdev_enable_event_code((*evdev), EV_KEY, BTN_2, NULL);
  345. libevdev_enable_event_code((*evdev), EV_KEY, BTN_3, NULL);
  346. break;
  347. case PRODUCT_ID_AM1107:
  348. case PRODUCT_ID_AM1209:
  349. libevdev_enable_event_code((*evdev), EV_KEY, BTN_0, NULL);
  350. libevdev_enable_event_code((*evdev), EV_KEY, BTN_1, NULL);
  351. libevdev_enable_event_code((*evdev), EV_KEY, BTN_2, NULL);
  352. libevdev_enable_event_code((*evdev), EV_KEY, BTN_3, NULL);
  353. libevdev_enable_event_code((*evdev), EV_KEY, BTN_4, NULL);
  354. libevdev_enable_event_code((*evdev), EV_KEY, BTN_5, NULL);
  355. libevdev_enable_event_code((*evdev), EV_KEY, BTN_6, NULL);
  356. libevdev_enable_event_code((*evdev), EV_KEY, BTN_7, NULL);
  357. break;
  358. default:
  359. // do nothing
  360. break;
  361. }
  362. // set up libevdev device name strings
  363. switch(desc.idProduct) {
  364. // cases are in ID order
  365. case PRODUCT_ID_AM3M:
  366. // space between Art and Master intentional
  367. libevdev_set_name((*evdev), "Hanvon Art Master III");
  368. break;
  369. case PRODUCT_ID_AM0806:
  370. libevdev_set_name((*evdev), "Hanvon ArtMaster AM0806");
  371. break;
  372. case PRODUCT_ID_AM0605:
  373. libevdev_set_name((*evdev), "Hanvon ArtMaster AM0605");
  374. break;
  375. case PRODUCT_ID_AM1107:
  376. // space between Art and Master intentional
  377. libevdev_set_name((*evdev), "Hanvon Art Master AM1107");
  378. break;
  379. case PRODUCT_ID_AM1209:
  380. libevdev_set_name((*evdev), "Hanvon ArtMaster AM1209");
  381. break;
  382. case PRODUCT_ID_RL0604:
  383. libevdev_set_name((*evdev), "Hanvon Rollick 0604");
  384. break;
  385. case PRODUCT_ID_RL0504:
  386. libevdev_set_name((*evdev), "Hanvon Rollick 0504");
  387. break;
  388. case PRODUCT_ID_GP0806:
  389. libevdev_set_name((*evdev), "Hanvon Graphicpal 0806");
  390. break;
  391. case PRODUCT_ID_GP0806B:
  392. libevdev_set_name((*evdev), "Hanvon Graphicpal 0806B");
  393. break;
  394. case PRODUCT_ID_GP0605:
  395. libevdev_set_name((*evdev), "Hanvon Graphicpal 0605");
  396. break;
  397. case PRODUCT_ID_GP0605A:
  398. libevdev_set_name((*evdev), "Hanvon Graphicpal 0605A");
  399. break;
  400. case PRODUCT_ID_GP0504:
  401. libevdev_set_name((*evdev), "Hanvon Graphicpal 0504");
  402. break;
  403. case PRODUCT_ID_NXS1513:
  404. libevdev_set_name((*evdev), "Hanvon Nilox NXS1513");
  405. break;
  406. case PRODUCT_ID_GP0906:
  407. libevdev_set_name((*evdev), "Hanvon Graphicpal 0906");
  408. break;
  409. case PRODUCT_ID_APPIV0906:
  410. libevdev_set_name((*evdev), "Hanvon Art Painter Pro APPIV0906");
  411. break;
  412. }
  413. int err = libevdev_uinput_create_from_device(
  414. (*evdev), LIBEVDEV_UINPUT_OPEN_MANAGED, uidev
  415. );
  416. printf("Initializing controls status: %x, \n", uidev);
  417. free(abs);
  418. return is_ok;
  419. }
  420. // GLOBAL: for main and hotplug_callback
  421. static int HOT_COUNT = 0;
  422. static struct libusb_transfer *TX = NULL;
  423. void loop() {
  424. int status = 0;
  425. int LAST_HOT_COUNT = 0;
  426. while (1) {
  427. if (TX != NULL && TX->dev_handle != 0) {
  428. status = libusb_submit_transfer(TX);
  429. }
  430. if (status < 0 && LAST_HOT_COUNT != HOT_COUNT) {
  431. printf("\nwarning: usb transfer status = %i\n", status);
  432. //continue;
  433. }
  434. LAST_HOT_COUNT = HOT_COUNT;
  435. libusb_handle_events_completed(NULL, NULL);
  436. }
  437. }
  438. // TODO: make scuti explain why this is called twice
  439. int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev,
  440. libusb_hotplug_event event, void *user_data) {
  441. static libusb_device_handle *dev_handle;
  442. static struct libevdev_uinput *uidev = NULL;
  443. static struct libevdev *evdev = NULL;
  444. struct libusb_device_descriptor desc;
  445. int rc;
  446. libusb_get_device_descriptor(dev, &desc);
  447. if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
  448. // check if the device supported
  449. libusb_device *devs[1] = {dev};
  450. int index = find_device(devs, 1);
  451. if (index < 0) {
  452. printf("INFO: Detected Hanvon device, but it is not supported.\n");
  453. } else {
  454. // libusb functions that accept libusb_device = SAFE
  455. // submit asynchronous transfers = SAFE
  456. // other functions that accept device handle = NOT SAFE
  457. libusb_open(dev, &dev_handle);
  458. if (init_ctrl(dev, &evdev, &uidev) < 0) {
  459. printf("Error: Could not initialize controls.\n");
  460. exit(EXIT_FAILURE);
  461. }
  462. const int ENDPOINT_ADDR = 0x81; // bEndpointAddress from lsusb -v
  463. //AM_PACKET_LEN = 10; // wMaxPacketSize from lsusb -v
  464. unsigned char buffer[AM_PACKET_LEN];
  465. TX = libusb_alloc_transfer(0);
  466. libusb_fill_interrupt_transfer(
  467. TX,
  468. dev_handle,
  469. ENDPOINT_ADDR,
  470. buffer,
  471. AM_PACKET_LEN,
  472. callback_default,
  473. uidev, // extra data to send in tx
  474. 130 // timeout in milliseconds
  475. );
  476. }
  477. } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
  478. if (dev_handle) {
  479. libusb_close(dev_handle);
  480. dev_handle = NULL;
  481. libevdev_uinput_destroy(uidev);
  482. libusb_free_transfer(TX);
  483. TX = NULL;
  484. }
  485. } else {
  486. printf ("INFO: Device left, but handle did not exist.\n");
  487. }
  488. HOT_COUNT++;
  489. return 0;
  490. }
  491. int main() {
  492. #define UNREF_DEVICE 1
  493. #define KEEP_DEVICE_REF 0
  494. int r = libusb_init(NULL);
  495. if (r < 0) {
  496. return r;
  497. }
  498. libusb_hotplug_callback_handle callback_handle;
  499. int rc;
  500. rc = libusb_hotplug_register_callback(
  501. NULL,
  502. LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
  503. LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
  504. 0,
  505. VENDOR_ID_HANVON, // VENDOR
  506. LIBUSB_HOTPLUG_MATCH_ANY, // PRODUCT
  507. LIBUSB_HOTPLUG_MATCH_ANY, // DEV CLASS
  508. hotplug_callback,
  509. NULL,
  510. &callback_handle
  511. );
  512. if (rc != LIBUSB_SUCCESS) {
  513. printf("Error creating a hotplug callback\n");
  514. libusb_exit(NULL);
  515. return EXIT_FAILURE;
  516. }
  517. loop();
  518. return 0;
  519. }