Merge branch 'merge/default-handler' of scuti/hanvon-linux into libusb-port

This commit is contained in:
scuti 2021-08-29 16:23:28 -07:00 committed by Gitea
commit bcde62a8ae
3 changed files with 232 additions and 49 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
#
# Project Executable
#
hvlusb
# #
# NOTE! Don't add files that are generated in specific # NOTE! Don't add files that are generated in specific
# subdirectories here. Add them in the ".gitignore" file # subdirectories here. Add them in the ".gitignore" file

View File

@ -1,7 +1,7 @@
# Hanvon Tablet Drivers # 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 ## Building
make make
@ -13,11 +13,11 @@ Run the output executable from a terminal with sudo (preferrably in the backgrou
## Supported Hardware ## 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 Rollick: RL0604, RL0504

View File

@ -24,7 +24,6 @@
#include <libevdev/libevdev.h> #include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h> #include <libevdev/libevdev-uinput.h>
// #include <asm/unaligned.h>
#define STATE_SUCCESS 0 #define STATE_SUCCESS 0
#define STATE_NOT_FOUND 1 #define STATE_NOT_FOUND 1
@ -47,16 +46,26 @@
#define PRODUCT_ID_APPIV0906 0x8532 #define PRODUCT_ID_APPIV0906 0x8532
#define AM_PACKET_LEN 10 #define AM_PACKET_LEN 10
#define AM_RESOLUTION 40
//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_WHEEL_THRESHOLD 4 #define AM_WHEEL_THRESHOLD 4
#define AM_MAX_TILT_X 0x3f #define AM_MAX_ABS_X 0x27DE
#define AM_MAX_TILT_Y 0x7f #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_MAX_PRESSURE 0x400
#define APPIV_MAX_ABS_X 0x5750
#define APPIV_MAX_ABS_Y 0x5750
#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 { struct hanvon_message {
unsigned char msgtype; unsigned char msgtype;
unsigned char is_move; unsigned char is_move;
@ -67,6 +76,9 @@ struct hanvon_message {
unsigned char y_tilt; unsigned char y_tilt;
}; };
// GLOBAL
int wheel_position;
int find_device(libusb_device **list, unsigned int count) { int find_device(libusb_device **list, unsigned int count) {
if (count < 0) { if (count < 0) {
return -1; return -1;
@ -113,16 +125,54 @@ void callback(struct libusb_transfer *transfer) {
display_packets(data); 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; unsigned char *data = tx -> buffer;
struct hanvon_message *msg = (struct hanvon_message *)tx -> buffer; struct hanvon_message *msg = (struct hanvon_message *)tx -> buffer;
int err = 0; int err = 0;
struct libevdev_uinput *ud = tx -> user_data; struct libevdev_uinput *ud = tx -> user_data;
switch(msg->msgtype) { switch(msg->msgtype) {
case 0x01: case BUTTON_EVENT_GP:
display_packets(data); 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; break;
case 0x02: case PEN_EVENT:
/* is_move values: /* is_move values:
0x80: near, 0x02: button press 0x80: near, 0x02: button press
0x10: floating, 0x01: touching */ 0x10: floating, 0x01: touching */
@ -146,7 +196,7 @@ void callback_gp0504 (struct libusb_transfer *tx) { // for callback
if(err) { DEBUG("err: %d\n",err); } if(err) { DEBUG("err: %d\n",err); }
} }
err = libevdev_uinput_write_event( 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); } if(err) { DEBUG("err: %d\n",err); }
err = libevdev_uinput_write_event( err = libevdev_uinput_write_event(
@ -168,13 +218,48 @@ void callback_gp0504 (struct libusb_transfer *tx) { // for callback
// data[1]: // data[1]:
// 0x10 = lift, 0x90 = close, 0x91 = press // 0x10 = lift, 0x90 = close, 0x91 = press
// 0x12 = btn (lift), 0x92 = btn (close), 0x93 = btn (press) // 0x12 = btn (lift), 0x92 = btn (close), 0x93 = btn (press)
display_packets(data);
break; 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: default:
display_packets(data); // do nothing
break; break;
} }
// always display packets
display_packets(data);
err += libevdev_uinput_write_event(ud, EV_SYN, SYN_REPORT, 0); err += libevdev_uinput_write_event(ud, EV_SYN, SYN_REPORT, 0);
if (err != 0) { if (err != 0) {
printf("error : gp0504, %i\n", err); printf("error : gp0504, %i\n", err);
@ -188,6 +273,7 @@ int init_ctrl(struct libusb_device * const d,
struct libevdev_uinput **uidev) { struct libevdev_uinput **uidev) {
struct input_absinfo *abs; struct input_absinfo *abs;
printf("init_ctrl: %x\n", uidev); printf("init_ctrl: %x\n", uidev);
wheel_position = AM_WHEEL_THRESHOLD - 1; // init global
if (d == NULL) { if (d == NULL) {
return -1; return -1;
} }
@ -195,10 +281,11 @@ int init_ctrl(struct libusb_device * const d,
struct libusb_device_descriptor desc; struct libusb_device_descriptor desc;
libusb_get_device_descriptor(d, &desc); libusb_get_device_descriptor(d, &desc);
(*evdev) = libevdev_new(); (*evdev) = libevdev_new();
// set up inputs all devices have
libevdev_enable_property((*evdev), INPUT_PROP_DIRECT); libevdev_enable_property((*evdev), INPUT_PROP_DIRECT);
libevdev_enable_event_type((*evdev), EV_SYN); libevdev_enable_event_type((*evdev), EV_SYN);
libevdev_enable_event_code((*evdev), EV_SYN, SYN_REPORT, NULL); 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_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_TOOL_PEN, NULL);
libevdev_enable_event_code((*evdev), EV_KEY, BTN_LEFT, NULL); // pen tap 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 // set up absolute x coordinate input
abs->value = 0x1000; abs->value = 0x1000;
abs->minimum = 0; 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->fuzz = 0;
abs->flat = 0; abs->flat = 0;
abs->resolution = 40; abs->resolution = AM_RESOLUTION;
if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_X, abs)<0) { if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_X, abs)<0) {
DEBUG("%s","failed to register absolute x\n"); DEBUG("%s","failed to register absolute x\n");
is_ok = -1; is_ok = -1;
@ -220,8 +314,15 @@ int init_ctrl(struct libusb_device * const d,
// set up absolute y coordinate input // set up absolute y coordinate input
abs->value = 0x1000; abs->value = 0x1000;
abs->minimum = 0; abs->minimum = 0;
abs->maximum = 0x1cfe; switch(desc.idProduct) {
abs->resolution = 40; 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) { if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_Y, abs)<0) {
DEBUG("%s","failed to register absolute y\n"); DEBUG("%s","failed to register absolute y\n");
is_ok = -1; is_ok = -1;
@ -253,15 +354,91 @@ int init_ctrl(struct libusb_device * const d,
DEBUG("%s","failed to register y tilt\n"); DEBUG("%s","failed to register y tilt\n");
is_ok = -1; 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) { switch(desc.idProduct) {
case PRODUCT_ID_GP0504: case PRODUCT_ID_AM3M:
libevdev_set_name((*evdev), "Hanvon Graphicpal GP0504"); 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; break;
case PRODUCT_ID_GP0906: 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; break;
case PRODUCT_ID_APPIV0906: default:
// do nothing
break; 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 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( int err = libevdev_uinput_create_from_device(
(*evdev), LIBEVDEV_UINPUT_OPEN_MANAGED, uidev (*evdev), LIBEVDEV_UINPUT_OPEN_MANAGED, uidev
); );
@ -294,12 +471,13 @@ int handle_device_lusb(libusb_device *d) {
// Allocate memory for transfer, configure, then submit // Allocate memory for transfer, configure, then submit
tx = libusb_alloc_transfer(0); tx = libusb_alloc_transfer(0);
libusb_fill_interrupt_transfer( tx, libusb_fill_interrupt_transfer(
tx,
h, h,
ENDPOINT_ADDR, ENDPOINT_ADDR,
buffer, buffer,
AM_PACKET_LEN, AM_PACKET_LEN,
callback_gp0504, callback_default,
uidev, // extra data to send in tx uidev, // extra data to send in tx
130); // timeout in milliseconds 130); // timeout in milliseconds
do { do {