From f79b5d85edbb259d08ddbe01f5ed2c61b9ee267c Mon Sep 17 00:00:00 2001 From: surkhe Date: Sun, 29 Aug 2021 16:19:04 -0700 Subject: [PATCH] Generalized, default handles GP0504, others MAY work --- .gitignore | 5 + README.md | 8 +- hanvon-libusb.c | 268 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 232 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index b3e6aea..aac3a37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# +# Project Executable +# +hvlusb + # # NOTE! Don't add files that are generated in specific # subdirectories here. Add them in the ".gitignore" file diff --git a/README.md b/README.md index d39527f..7bdb2a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Hanvon Tablet Drivers -Userspace driver for Hanvon pen tablets adapted from the original Linux kernel driver. Supports tablet features such as pen coordinates, x and y tilts, proximity and pressure detection. +Userspace driver for Hanvon pen tablets adapted from the original Linux kernel driver. Supports tablet features such as pen coordinates, x and y tilt angle, hover detection, pressure detection, and button input. ## Building make @@ -13,11 +13,11 @@ Run the output executable from a terminal with sudo (preferrably in the backgrou ## Supported Hardware -Currently, only GP0504 is tested and supported. +All tablets supported by the original driver should work with this libusb driver but only the GP0504 has been tested. -The original series of supported hardware: +The original driver supported the following models: -Artmaster I: AM3M, AM0605, AM0806, AM1107, AM1209 +ArtMaster: AM3M, AM0605, AM0806, AM1107, AM1209 Rollick: RL0604, RL0504 diff --git a/hanvon-libusb.c b/hanvon-libusb.c index 58b1dc2..8c00497 100644 --- a/hanvon-libusb.c +++ b/hanvon-libusb.c @@ -10,7 +10,7 @@ * Revision: none * Compiler: gcc * -* Maintained by: scuti@teknik.io +* Maintained by: scuti@teknik.io * surkeh@protonmail.com * * ===================================================================================== @@ -24,38 +24,47 @@ #include #include -// #include -#define STATE_SUCCESS 0 -#define STATE_NOT_FOUND 1 +#define STATE_SUCCESS 0 +#define STATE_NOT_FOUND 1 -#define VENDOR_ID_HANVON 0x0b57 -#define PRODUCT_ID_AM3M 0x8528 -#define PRODUCT_ID_AM0806 0x8502 -#define PRODUCT_ID_AM0605 0x8503 -#define PRODUCT_ID_AM1107 0x8505 -#define PRODUCT_ID_AM1209 0x8501 -#define PRODUCT_ID_RL0604 0x851f -#define PRODUCT_ID_RL0504 0x851d -#define PRODUCT_ID_GP0806 0x8039 -#define PRODUCT_ID_GP0806B 0x8511 -#define PRODUCT_ID_GP0605 0x8512 -#define PRODUCT_ID_GP0605A 0x803a -#define PRODUCT_ID_GP0504 0x8037 -#define PRODUCT_ID_NXS1513 0x8030 -#define PRODUCT_ID_GP0906 0x8521 -#define PRODUCT_ID_APPIV0906 0x8532 +#define VENDOR_ID_HANVON 0x0b57 +#define PRODUCT_ID_AM3M 0x8528 +#define PRODUCT_ID_AM0806 0x8502 +#define PRODUCT_ID_AM0605 0x8503 +#define PRODUCT_ID_AM1107 0x8505 +#define PRODUCT_ID_AM1209 0x8501 +#define PRODUCT_ID_RL0604 0x851f +#define PRODUCT_ID_RL0504 0x851d +#define PRODUCT_ID_GP0806 0x8039 +#define PRODUCT_ID_GP0806B 0x8511 +#define PRODUCT_ID_GP0605 0x8512 +#define PRODUCT_ID_GP0605A 0x803a +#define PRODUCT_ID_GP0504 0x8037 +#define PRODUCT_ID_NXS1513 0x8030 +#define PRODUCT_ID_GP0906 0x8521 +#define PRODUCT_ID_APPIV0906 0x8532 -#define AM_PACKET_LEN 10 +#define AM_PACKET_LEN 10 +#define AM_RESOLUTION 40 +#define AM_WHEEL_THRESHOLD 4 -//static int lbuttons[]={BTN_0,BTN_1,BTN_2,BTN_3}; /* reported on all AMs */ -//static int rbuttons[]={BTN_4,BTN_5,BTN_6,BTN_7}; /* reported on AM1107+ */ +#define AM_MAX_ABS_X 0x27DE +#define AM_MAX_ABS_Y 0x1CFE +#define AM_MAX_TILT_X 0x3F +#define AM_MAX_TILT_Y 0x7F +#define AM_MAX_PRESSURE 0x400 -#define AM_WHEEL_THRESHOLD 4 +#define APPIV_MAX_ABS_X 0x5750 +#define APPIV_MAX_ABS_Y 0x5750 -#define AM_MAX_TILT_X 0x3f -#define AM_MAX_TILT_Y 0x7f -#define AM_MAX_PRESSURE 0x400 +#define BUTTON_EVENT_GP 0x01 +#define PEN_EVENT 0x02 +#define BUTTON_EVENT_0906 0x0C + + +static int lbuttons[]={BTN_0,BTN_1,BTN_2,BTN_3}; /* reported on all AMs */ +static int rbuttons[]={BTN_4,BTN_5,BTN_6,BTN_7}; /* reported on AM1107+ */ struct hanvon_message { unsigned char msgtype; @@ -67,6 +76,9 @@ struct hanvon_message { unsigned char y_tilt; }; +// GLOBAL +int wheel_position; + int find_device(libusb_device **list, unsigned int count) { if (count < 0) { return -1; @@ -77,7 +89,7 @@ int find_device(libusb_device **list, unsigned int count) { libusb_device *t = list[i]; libusb_get_device_descriptor(list[i], &desc); if (desc.idVendor == VENDOR_ID_HANVON) { - switch(desc.idProduct) { + switch(desc.idProduct) { default: break; case PRODUCT_ID_AM0806: @@ -113,16 +125,54 @@ void callback(struct libusb_transfer *transfer) { display_packets(data); } -void callback_gp0504 (struct libusb_transfer *tx) { // for callback +static inline void report_buttons( struct libevdev_uinput *ud, + int buttons[], + unsigned char data) +{ + int err = 0; + if((data & 0xf0) == 0xa0) { + // TODO test that these are the correct buttons and all buttons are covered + err = libevdev_uinput_write_event(ud, EV_KEY, buttons[1], (data & 0x02)); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event(ud, EV_KEY, buttons[2], (data & 0x04)); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event(ud, EV_KEY, buttons[3], (data & 0x08)); + if(err) { DEBUG("err: %d\n",err); } + } else if(data <= 0x3f) { /* slider area active */ + int delta = data - wheel_position; + if(abs(delta) < AM_WHEEL_THRESHOLD) { + err = libevdev_uinput_write_event(ud, EV_REL, REL_WHEEL, delta); // TODO test delta as input + if(err) { DEBUG("err: %d\n",err); } + wheel_position = data; + } + + } +} + + +// NOTE: +// Judging by the original driver, this should work for all but may not work +// for the APPIV0906. Possibly needs little endian for APPIV0906 x and y data +// but we don't have any means of testing this without that tablet. +// NOTE: +// Left and right mouse click should work for all but additional buttons are +// not supported by the default handler. +void callback_default (struct libusb_transfer *tx) { // for callback unsigned char *data = tx -> buffer; struct hanvon_message *msg = (struct hanvon_message *)tx -> buffer; int err = 0; struct libevdev_uinput *ud = tx -> user_data; switch(msg->msgtype) { - case 0x01: - display_packets(data); + case BUTTON_EVENT_GP: + if(data[1] == 0x55) { // left side buttons + report_buttons(ud, lbuttons, msg->x_movement); // button pressed data in same place as position data + } + + if(data[3] == 0xAA) { // right side buttons (am1107, am1209 + report_buttons(ud, rbuttons, msg->y_movement); // button pressed data in same place as position data + } break; - case 0x02: + case PEN_EVENT: /* is_move values: 0x80: near, 0x02: button press 0x10: floating, 0x01: touching */ @@ -146,7 +196,7 @@ void callback_gp0504 (struct libusb_transfer *tx) { // for callback if(err) { DEBUG("err: %d\n",err); } } err = libevdev_uinput_write_event( - ud, EV_ABS, ABS_PRESSURE, msg->pressure + ud, EV_ABS, ABS_PRESSURE, msg->pressure * 8 // reference original driver ); if(err) { DEBUG("err: %d\n",err); } err = libevdev_uinput_write_event( @@ -168,13 +218,48 @@ void callback_gp0504 (struct libusb_transfer *tx) { // for callback // data[1]: // 0x10 = lift, 0x90 = close, 0x91 = press // 0x12 = btn (lift), 0x92 = btn (close), 0x93 = btn (press) - display_packets(data); break; + case BUTTON_EVENT_0906: + // TODO confirm this is the byte that contains button flags + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_0, (msg->is_move & 0x0100) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_1, (msg->is_move & 0x0200) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_2, (msg->is_move & 0x0400) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_3, (msg->is_move & 0x0800) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_4, (msg->is_move & 0x1000) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_5, (msg->is_move & 0x2000) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_6, (msg->is_move & 0x4000) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } + err = libevdev_uinput_write_event( + ud, EV_KEY, BTN_7, (msg->is_move & 0x8000) / 2 + ); + if(err) { DEBUG("err: %d\n",err); } default: - display_packets(data); + // do nothing break; - } + // always display packets + display_packets(data); + err += libevdev_uinput_write_event(ud, EV_SYN, SYN_REPORT, 0); if (err != 0) { printf("error : gp0504, %i\n", err); @@ -188,6 +273,7 @@ int init_ctrl(struct libusb_device * const d, struct libevdev_uinput **uidev) { struct input_absinfo *abs; printf("init_ctrl: %x\n", uidev); + wheel_position = AM_WHEEL_THRESHOLD - 1; // init global if (d == NULL) { return -1; } @@ -195,10 +281,11 @@ int init_ctrl(struct libusb_device * const d, struct libusb_device_descriptor desc; libusb_get_device_descriptor(d, &desc); (*evdev) = libevdev_new(); + + // set up inputs all devices have libevdev_enable_property((*evdev), INPUT_PROP_DIRECT); libevdev_enable_event_type((*evdev), EV_SYN); libevdev_enable_event_code((*evdev), EV_SYN, SYN_REPORT, NULL); - // every tablet has these features libevdev_enable_event_type((*evdev), EV_KEY); // enable pen button libevdev_enable_event_code((*evdev), EV_KEY, BTN_TOOL_PEN, NULL); libevdev_enable_event_code((*evdev), EV_KEY, BTN_LEFT, NULL); // pen tap @@ -209,10 +296,17 @@ int init_ctrl(struct libusb_device * const d, // set up absolute x coordinate input abs->value = 0x1000; abs->minimum = 0; - abs->maximum = 0x27DE; + switch(desc.idProduct) { + case PRODUCT_ID_APPIV0906: + abs->maximum = APPIV_MAX_ABS_X; + break; + default: + abs->maximum = AM_MAX_ABS_X; + break; + } abs->fuzz = 0; abs->flat = 0; - abs->resolution = 40; + abs->resolution = AM_RESOLUTION; if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_X, abs)<0) { DEBUG("%s","failed to register absolute x\n"); is_ok = -1; @@ -220,8 +314,15 @@ int init_ctrl(struct libusb_device * const d, // set up absolute y coordinate input abs->value = 0x1000; abs->minimum = 0; - abs->maximum = 0x1cfe; - abs->resolution = 40; + switch(desc.idProduct) { + case PRODUCT_ID_APPIV0906: + abs->maximum = APPIV_MAX_ABS_Y; + break; + default: + abs->maximum = AM_MAX_ABS_Y; + break; + } + abs->resolution = AM_RESOLUTION; if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_Y, abs)<0) { DEBUG("%s","failed to register absolute y\n"); is_ok = -1; @@ -253,15 +354,91 @@ int init_ctrl(struct libusb_device * const d, DEBUG("%s","failed to register y tilt\n"); is_ok = -1; } + + // Scroll wheel is NOT universal + if(libevdev_enable_event_code((*evdev), EV_REL, REL_WHEEL, NULL)<0) { + DEBUG("%s","failed to register scroll wheel\n"); + is_ok = -1; + } + + // set up device-specific inputs switch(desc.idProduct) { + case PRODUCT_ID_AM3M: + case PRODUCT_ID_AM0806: + case PRODUCT_ID_AM0605: + libevdev_enable_event_code((*evdev), EV_KEY, BTN_0, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_1, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_2, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_3, NULL); + break; + case PRODUCT_ID_AM1107: + case PRODUCT_ID_AM1209: + libevdev_enable_event_code((*evdev), EV_KEY, BTN_0, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_1, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_2, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_3, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_4, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_5, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_6, NULL); + libevdev_enable_event_code((*evdev), EV_KEY, BTN_7, NULL); + break; + default: + // do nothing + break; + } + + // set up libevdev device name strings + switch(desc.idProduct) { + // cases are in ID order + case PRODUCT_ID_AM3M: + // space between Art and Master intentional + libevdev_set_name((*evdev), "Hanvon Art Master III"); + break; + case PRODUCT_ID_AM0806: + libevdev_set_name((*evdev), "Hanvon ArtMaster AM0806"); + break; + case PRODUCT_ID_AM0605: + libevdev_set_name((*evdev), "Hanvon ArtMaster AM0605"); + break; + case PRODUCT_ID_AM1107: + // space between Art and Master intentional + libevdev_set_name((*evdev), "Hanvon Art Master AM1107"); + break; + case PRODUCT_ID_AM1209: + libevdev_set_name((*evdev), "Hanvon ArtMaster AM1209"); + break; + case PRODUCT_ID_RL0604: + libevdev_set_name((*evdev), "Hanvon Rollick 0604"); + break; + case PRODUCT_ID_RL0504: + libevdev_set_name((*evdev), "Hanvon Rollick 0504"); + break; + case PRODUCT_ID_GP0806: + libevdev_set_name((*evdev), "Hanvon Graphicpal 0806"); + break; + case PRODUCT_ID_GP0806B: + libevdev_set_name((*evdev), "Hanvon Graphicpal 0806B"); + break; + case PRODUCT_ID_GP0605: + libevdev_set_name((*evdev), "Hanvon Graphicpal 0605"); + break; + case PRODUCT_ID_GP0605A: + libevdev_set_name((*evdev), "Hanvon Graphicpal 0605A"); + break; case PRODUCT_ID_GP0504: - libevdev_set_name((*evdev), "Hanvon Graphicpal GP0504"); + libevdev_set_name((*evdev), "Hanvon Graphicpal 0504"); + break; + case PRODUCT_ID_NXS1513: + libevdev_set_name((*evdev), "Hanvon Nilox NXS1513"); break; case PRODUCT_ID_GP0906: + libevdev_set_name((*evdev), "Hanvon Graphicpal 0906"); break; case PRODUCT_ID_APPIV0906: + libevdev_set_name((*evdev), "Hanvon Art Painter Pro APPIV0906"); break; } + int err = libevdev_uinput_create_from_device( (*evdev), LIBEVDEV_UINPUT_OPEN_MANAGED, uidev ); @@ -275,7 +452,7 @@ int handle_device_lusb(libusb_device *d) { int status = libusb_open(d, &h); if (status < 0 || h == NULL) { printf("Error opening device, %i.\n", status); - return 0; + return 0; } struct libevdev *evdev = NULL; struct libevdev_uinput *uidev = NULL; @@ -294,12 +471,13 @@ int handle_device_lusb(libusb_device *d) { // Allocate memory for transfer, configure, then submit tx = libusb_alloc_transfer(0); - libusb_fill_interrupt_transfer( tx, + libusb_fill_interrupt_transfer( + tx, h, ENDPOINT_ADDR, buffer, AM_PACKET_LEN, - callback_gp0504, + callback_default, uidev, // extra data to send in tx 130); // timeout in milliseconds do {