hanvon-linux/hanvon-libusb.c
2021-07-08 13:30:01 -07:00

365 lines
11 KiB
C

/*
* =====================================================================================
*
* 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 <stdlib.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
// #include <asm/unaligned.h>
#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;
}