123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- /*
- * =====================================================================================
- *
- * Filename: hanvon-libusb.c
- *
- * Description: libusb Hanvon tablet driver
- *
- * Version: 0.1
- * Created: 08/17/2020 04:05:14 PM
- * Revision: none
- * Compiler: gcc
- *
- * Maintained by: scuti@teknik.io
- * surkeh@protonmail.com
- *
- * =====================================================================================
- */
- #define DEBUG(msg,...) fprintf(stderr,"%s(%d): " msg , __FILE__,__LINE__,__VA_ARGS__)
- #include <stdlib.h>
- #include <stdio.h>
- #include <libusb-1.0/libusb.h>
- #include <libevdev/libevdev.h>
- #include <libevdev/libevdev-uinput.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
- #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;
- unsigned char is_move;
- unsigned short x_movement;
- unsigned short y_movement;
- unsigned char pressure;
- unsigned char x_tilt;
- unsigned char y_tilt;
- };
- // GLOBAL
- int wheel_position;
- int find_device(libusb_device **list, unsigned int count) {
- if (count < 0) {
- return -1;
- }
- int found = -1;
- struct libusb_device_descriptor desc;
- for (unsigned int i = 0; i < count; i++) {
- libusb_device *t = list[i];
- libusb_get_device_descriptor(list[i], &desc);
- if (desc.idVendor == VENDOR_ID_HANVON) {
- switch(desc.idProduct) {
- default:
- break;
- case PRODUCT_ID_AM0806:
- case PRODUCT_ID_AM0605:
- case PRODUCT_ID_AM1107:
- case PRODUCT_ID_AM1209:
- case PRODUCT_ID_RL0604:
- case PRODUCT_ID_RL0504:
- case PRODUCT_ID_GP0806:
- case PRODUCT_ID_GP0806B:
- case PRODUCT_ID_GP0605:
- case PRODUCT_ID_GP0605A:
- case PRODUCT_ID_GP0504:
- case PRODUCT_ID_NXS1513:
- case PRODUCT_ID_GP0906:
- case PRODUCT_ID_APPIV0906:
- return i;
- } // end switch
- } // end if
- } // end for
- return found;
- }
- void display_packets(const unsigned char* buf) {
- for(int i = 0; i < AM_PACKET_LEN; i++) {
- fprintf(stderr,"0x%x, ", buf[i]);
- }
- fprintf(stderr,"\r");
- }
- void callback(struct libusb_transfer *transfer) {
- unsigned char *data = transfer -> buffer;
- display_packets(data);
- }
- 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 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 PEN_EVENT:
- /* is_move values:
- 0x80: near, 0x02: button press
- 0x10: floating, 0x01: touching */
- err = libevdev_uinput_write_event(
- ud, EV_KEY, BTN_TOOL_PEN, msg->is_move & (0x80|0x10)
- );
- if(err) { DEBUG("err: %d\n",err); }
- if(msg->is_move & (0x80|0x10)) {
- msg->x_movement = htobe16(msg->x_movement);
- //DEBUG("Set X to %x\n",msg->x_movement);
- err = libevdev_uinput_write_event(
- ud, EV_ABS, ABS_X, msg->x_movement
- );
- if(err) { DEBUG("err: %d\n",err); }
- msg->y_movement = htobe16(msg->y_movement);
- //DEBUG("Set Y to %x\n",msg->y_movement);
- err = libevdev_uinput_write_event(
- ud, EV_ABS, ABS_Y, msg->y_movement
- );
- if(err) { DEBUG("err: %d\n",err); }
- }
- err = libevdev_uinput_write_event(
- ud, EV_ABS, ABS_PRESSURE, msg->pressure * 8 // reference original driver
- );
- if(err) { DEBUG("err: %d\n",err); }
- err = libevdev_uinput_write_event(
- ud, EV_ABS, ABS_TILT_X, msg->x_tilt
- );
- if(err) { DEBUG("err: %d\n",err); }
- err = libevdev_uinput_write_event(
- ud, EV_ABS, ABS_TILT_Y, msg->y_tilt
- );
- if(err) { DEBUG("err: %d\n",err); }
- err = libevdev_uinput_write_event(
- ud, EV_KEY, BTN_LEFT, msg->is_move & 0x01
- );
- if(err) { DEBUG("err: %d\n",err); }
- err = libevdev_uinput_write_event(
- ud, EV_KEY, BTN_RIGHT, (msg->is_move & 0x02) / 2
- );
- if(err) { DEBUG("err: %d\n",err); }
- // data[1]:
- // 0x10 = lift, 0x90 = close, 0x91 = press
- // 0x12 = btn (lift), 0x92 = btn (close), 0x93 = btn (press)
- 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:
- // 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);
- }
- return;
- }
- // https://www.freedesktop.org/software/libevdev/doc/latest/group__kernel.html
- int init_ctrl(struct libusb_device * const d,
- struct libevdev **evdev,
- 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;
- }
- int is_ok = 0;
- 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);
- 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
- libevdev_enable_event_code((*evdev), EV_KEY, BTN_RIGHT, NULL); // pen button
- // enable absolute position, pressure, tilt
- libevdev_enable_event_type((*evdev), EV_ABS);
- abs = malloc(sizeof(struct input_absinfo));
- // set up absolute x coordinate input
- abs->value = 0x1000;
- abs->minimum = 0;
- 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 = 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;
- }
- // set up absolute y coordinate input
- abs->value = 0x1000;
- abs->minimum = 0;
- 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;
- }
- // set up pressure input
- abs -> value = 0;
- abs -> minimum = 0;
- abs -> maximum = AM_MAX_PRESSURE;
- abs -> resolution = 0;
- if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_PRESSURE, abs)<0) {
- DEBUG("%s","failed to register pressure\n");
- is_ok = -1;
- }
- // set up tilt x input
- abs -> value = 0;
- abs -> minimum = 0;
- abs -> maximum = AM_MAX_TILT_X;
- abs -> resolution = 0;
- if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_TILT_X, abs)<0) {
- DEBUG("%s","failed to register x tilt\n");
- is_ok = -1;
- }
- // set up tilt y input
- abs -> value = 0;
- abs -> minimum = 0;
- abs -> maximum = AM_MAX_TILT_Y;
- abs -> resolution = 0;
- if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_TILT_Y, abs)<0) {
- 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 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
- );
- printf("Initializing controls status: %x, \n", uidev);
- free(abs);
- return is_ok;
- }
- // GLOBAL: for main and hotplug_callback
- static int HOT_COUNT = 0;
- static struct libusb_transfer *TX = NULL;
- void loop() {
- int status = 0;
- int LAST_HOT_COUNT = 0;
- while (1) {
- if (TX != NULL && TX->dev_handle != 0) {
- status = libusb_submit_transfer(TX);
- }
- if (status < 0 && LAST_HOT_COUNT != HOT_COUNT) {
- printf("\nwarning: usb transfer status = %i\n", status);
- //continue;
- }
- LAST_HOT_COUNT = HOT_COUNT;
- libusb_handle_events_completed(NULL, NULL);
- }
- }
- // TODO: make scuti explain why this is called twice
- int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event, void *user_data) {
- static libusb_device_handle *dev_handle;
- static struct libevdev_uinput *uidev = NULL;
- static struct libevdev *evdev = NULL;
- struct libusb_device_descriptor desc;
- int rc;
- libusb_get_device_descriptor(dev, &desc);
- if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
- // check if the device supported
- libusb_device *devs[1] = {dev};
- int index = find_device(devs, 1);
- if (index < 0) {
- printf("INFO: Detected Hanvon device, but it is not supported.\n");
- } else {
- // libusb functions that accept libusb_device = SAFE
- // submit asynchronous transfers = SAFE
- // other functions that accept device handle = NOT SAFE
- libusb_open(dev, &dev_handle);
- if (init_ctrl(dev, &evdev, &uidev) < 0) {
- printf("Error: Could not initialize controls.\n");
- exit(EXIT_FAILURE);
-
- }
- const int ENDPOINT_ADDR = 0x81; // bEndpointAddress from lsusb -v
- //AM_PACKET_LEN = 10; // wMaxPacketSize from lsusb -v
- unsigned char buffer[AM_PACKET_LEN];
- TX = libusb_alloc_transfer(0);
- libusb_fill_interrupt_transfer(
- TX,
- dev_handle,
- ENDPOINT_ADDR,
- buffer,
- AM_PACKET_LEN,
- callback_default,
- uidev, // extra data to send in tx
- 130 // timeout in milliseconds
- );
- }
- } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
- if (dev_handle) {
- libusb_close(dev_handle);
- dev_handle = NULL;
- libevdev_uinput_destroy(uidev);
- libusb_free_transfer(TX);
- TX = NULL;
- }
- } else {
- printf ("INFO: Device left, but handle did not exist.\n");
- }
- HOT_COUNT++;
- return 0;
- }
- int main() {
- #define UNREF_DEVICE 1
- #define KEEP_DEVICE_REF 0
- int r = libusb_init(NULL);
- if (r < 0) {
- return r;
- }
- libusb_hotplug_callback_handle callback_handle;
- int rc;
- rc = libusb_hotplug_register_callback(
- NULL,
- LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
- LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
- 0,
- VENDOR_ID_HANVON, // VENDOR
- LIBUSB_HOTPLUG_MATCH_ANY, // PRODUCT
- LIBUSB_HOTPLUG_MATCH_ANY, // DEV CLASS
- hotplug_callback,
- NULL,
- &callback_handle
- );
- if (rc != LIBUSB_SUCCESS) {
- printf("Error creating a hotplug callback\n");
- libusb_exit(NULL);
- return EXIT_FAILURE;
- }
- loop();
- return 0;
- }
|