/* * ===================================================================================== * * Filename: hanvon-libusb.c * * Description: Libusb GP0504 handler prototype * * Version: 0.1 * Created: 08/17/2020 04:05:14 PM * Revision: none * Compiler: gcc * * Author: surkeh@protonmail.com * * ===================================================================================== */ #define DEBUG(msg,...) fprintf(stderr,"%s(%d): " msg , __FILE__,__LINE__,__VA_ARGS__) #include #include #include #include #include // #include #define STATE_SUCCESS 0 #define STATE_NOT_FOUND 1 #define UNREF_DEVICE 1 #define KEEP_DEVICE_REF 0 #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 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; }; 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); // printf( "Dev%u ID %04x:%04x\n", (i), desc.idVendor, desc.idProduct ); 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,"\n"); } void callback(struct libusb_transfer *transfer) { unsigned char *data = transfer -> buffer; display_packets(data); } void callback_gp0504 (struct libusb_transfer *tx) { // for callback unsigned char *data = tx -> buffer; struct hanvon_message *msg = (struct hanvon_message *)tx -> buffer; unsigned short temp; int err = 0; struct libevdev_uinput *ud = tx -> user_data; switch(msg->msgtype) { case 0x01: display_packets(data); break; case 0x02: /*is_move values: 0x80: near 0x10: floating 0x02: button press 0x01: touching */ 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_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); if(err) { DEBUG("err: %d\n",err); } // pen touches surface //err += libevdev_uinput_write_event(ud, EV_KEY, BTN_LEFT, data[6] > 68); // data[1]: // 0x10 = lift, 0x90 = close, 0x91 = press // 0x12 = btn (lift), 0x92 = btn (close), 0x93 = btn (press) //err += libevdev_uinput_write_event(ud, EV_KEY, BTN_RIGHT, data[1] & 0x02); // if((data[1] & 0xf0) != 0) { // //absolute x // temp = data[2]; // temp <<= 8; // temp += data[3]; // err += libevdev_uinput_write_event(ud, EV_ABS, ABS_X, temp); // //absolute y // temp = data[4]; // temp <<= 8; // temp += data[5]; // err += libevdev_uinput_write_event(ud, EV_ABS, ABS_Y, temp); // } //else { display_packets(data); } break; default: display_packets(data); break; } 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); int is_ok = 0; if (d == NULL) { return is_ok; } struct libusb_device_descriptor desc; libusb_get_device_descriptor(d, &desc); (*evdev) = libevdev_new(); 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_LEFT, NULL); // pen tap libevdev_enable_event_code((*evdev), EV_KEY, BTN_RIGHT, NULL); // pen button libevdev_enable_event_type((*evdev), EV_ABS); // enable absolute position abs = malloc(sizeof(struct input_absinfo)); abs->value = 0x1000; abs->minimum = 0; abs->maximum = 0x27DE; abs->fuzz = 0; abs->flat = 0; abs->resolution = 40; if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_X, abs)<0) // pen button { DEBUG("%s","failed to register absolute x\n"); } abs = malloc(sizeof(struct input_absinfo)); abs->value = 0x1000; abs->minimum = 0; abs->maximum = 0x1cfe; abs->fuzz = 0; abs->flat = 0; abs->resolution = 40; if(libevdev_enable_event_code((*evdev), EV_ABS, ABS_Y, abs)<0) // pen button { DEBUG("%s","failed to register absolute x\n"); } switch(desc.idProduct) { case PRODUCT_ID_GP0504: libevdev_set_name((*evdev), "Hanvon Graphicpal GP0504"); break; case PRODUCT_ID_GP0906: break; case PRODUCT_ID_APPIV0906: break; } int err = libevdev_uinput_create_from_device((*evdev), LIBEVDEV_UINPUT_OPEN_MANAGED, uidev); printf("Initializing controls status: %x, \n", uidev); return is_ok; } int handle_device_lusb(libusb_device *d) { libusb_device_handle *h; int status = libusb_open(d, &h); if (status < 0 || h == NULL) { printf("Error opening device, %i.\n", status); return 0; } struct libevdev *evdev = NULL; struct libevdev_uinput *uidev = NULL; printf("handle_device: %x\n", uidev); init_ctrl(d, &evdev, &uidev); if (evdev == NULL || uidev == NULL) { printf("Error initializing controls, %x, %x.\n", evdev, uidev); return 0; } struct libusb_transfer *tx; const int ENDPOINT_ADDR = 0x81; // bEndpointAddress from lsusb -v // AM_PACKET_LEN = 10; // wMaxPacketSize from lsusb -v unsigned char buffer[AM_PACKET_LEN]; // Allocate memory for transfer, configure, then submit tx = libusb_alloc_transfer(0); libusb_fill_interrupt_transfer( tx, h, ENDPOINT_ADDR, buffer, AM_PACKET_LEN, callback_gp0504, uidev, // extra data to send in tx 130); // timeout in milliseconds do { status = libusb_submit_transfer(tx); if (status < 0 ) { //return status; //continue; } libusb_handle_events(NULL); } while (1); libevdev_uinput_destroy(uidev); return 0; } libusb_device *FindHanvon( libusb_context **context); int HandleData( void ); int main() { #define UNREF_DEVICE 1 #define KEEP_DEVICE_REF 0 libusb_device ** devs; int r = libusb_init(NULL); if (r < 0) { return r; } int count = libusb_get_device_list(NULL, &devs); if (count < 0) { libusb_exit(NULL); return count; } int index = find_device(devs, count); if (index < 0) { printf("Device not plugged in\n"); libusb_exit(NULL); return 0; } libusb_device *device = devs[index]; libusb_free_device_list (devs, UNREF_DEVICE); int s = handle_device_lusb(device); libusb_exit(NULL); return 0; } libusb_device *FindHanvon( libusb_context **context) { libusb_device **deviceList; libusb_device *found = NULL; ssize_t nDevices = libusb_get_device_list( context[0], &deviceList); if( nDevices > 0 ) { struct libusb_device_descriptor description; for( ssize_t i = 0; i < nDevices; i++ ) { libusb_device *device = deviceList[i]; libusb_get_device_descriptor(device, &description); //printf( "Dev%u ID %04x:%04x\n", (i + 1), description.idVendor, description.idProduct ); if( description.idVendor == VENDOR_ID_HANVON ) { switch( description.idProduct ) 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: found = device; break; } } printf( "\n\n" ); } libusb_free_device_list( deviceList, UNREF_DEVICE ); return found; }