Browse Source

Generalized, default handles GP0504, others MAY work

pull/10/head
surkhe 12 months ago
parent
commit
f79b5d85ed
  1. 5
      .gitignore
  2. 8
      README.md
  3. 280
      hanvon-libusb.c

5
.gitignore vendored

@ -1,3 +1,8 @@ @@ -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

8
README.md

@ -1,7 +1,7 @@ @@ -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 @@ -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

280
hanvon-libusb.c

@ -10,7 +10,7 @@ @@ -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 @@ @@ -24,38 +24,47 @@
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
// #include <asm/unaligned.h>
#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 AM_PACKET_LEN 10
//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_MAX_TILT_X 0x3f
#define AM_MAX_TILT_Y 0x7f
#define AM_MAX_PRESSURE 0x400
#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 AM_PACKET_LEN 10
#define AM_RESOLUTION 40
#define AM_WHEEL_THRESHOLD 4
#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 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 {
unsigned char msgtype;
@ -67,6 +76,9 @@ struct hanvon_message { @@ -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) { @@ -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) { @@ -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 @@ -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 @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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) { @@ -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) { @@ -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 {

Loading…
Cancel
Save