2019-02-10 13:13:50 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* HID driver for UC - Logic devices not fully compliant with HID standard
*
* Copyright ( c ) 2010 - 2014 Nikolai Kondrashov
* Copyright ( c ) 2013 Martin Rusko
*/
/*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*/
# include <linux/device.h>
# include <linux/hid.h>
# include <linux/module.h>
2019-02-10 13:13:54 +03:00
# include <linux/timer.h>
2019-02-10 13:13:50 +03:00
# include "usbhid/usbhid.h"
2019-02-10 13:13:51 +03:00
# include "hid-uclogic-params.h"
2019-02-10 13:13:50 +03:00
# include "hid-ids.h"
/* Driver data */
struct uclogic_drvdata {
2019-02-10 13:13:51 +03:00
/* Interface parameters */
struct uclogic_params params ;
/* Pointer to the replacement report descriptor. NULL if none. */
__u8 * desc_ptr ;
/*
* Size of the replacement report descriptor .
* Only valid if desc_ptr is not NULL
*/
unsigned int desc_size ;
2019-02-10 13:13:54 +03:00
/* Pen input device */
struct input_dev * pen_input ;
/* In-range timer */
struct timer_list inrange_timer ;
2019-02-10 13:14:04 +03:00
/* Last rotary encoder state, or U8_MAX for none */
u8 re_state ;
2019-02-10 13:13:50 +03:00
} ;
2019-02-10 13:13:54 +03:00
/**
* uclogic_inrange_timeout - handle pen in - range state timeout .
* Emulate input events normally generated when pen goes out of range for
* tablets which don ' t report that .
*
* @ t : The timer the timeout handler is attached to , stored in a struct
* uclogic_drvdata .
*/
static void uclogic_inrange_timeout ( struct timer_list * t )
{
struct uclogic_drvdata * drvdata = from_timer ( drvdata , t ,
inrange_timer ) ;
struct input_dev * input = drvdata - > pen_input ;
if ( input = = NULL )
return ;
input_report_abs ( input , ABS_PRESSURE , 0 ) ;
/* If BTN_TOUCH state is changing */
if ( test_bit ( BTN_TOUCH , input - > key ) ) {
input_event ( input , EV_MSC , MSC_SCAN ,
/* Digitizer Tip Switch usage */
0xd0042 ) ;
input_report_key ( input , BTN_TOUCH , 0 ) ;
}
input_report_key ( input , BTN_TOOL_PEN , 0 ) ;
input_sync ( input ) ;
}
2019-02-10 13:13:50 +03:00
static __u8 * uclogic_report_fixup ( struct hid_device * hdev , __u8 * rdesc ,
unsigned int * rsize )
{
struct uclogic_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
2019-02-10 13:13:51 +03:00
if ( drvdata - > desc_ptr ! = NULL ) {
rdesc = drvdata - > desc_ptr ;
* rsize = drvdata - > desc_size ;
2019-02-10 13:13:50 +03:00
}
return rdesc ;
}
2022-04-21 20:50:51 +03:00
static int uclogic_input_mapping ( struct hid_device * hdev ,
struct hid_input * hi ,
struct hid_field * field ,
struct hid_usage * usage ,
unsigned long * * bit ,
int * max )
{
struct uclogic_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
struct uclogic_params * params = & drvdata - > params ;
/* Discard invalid pen usages */
if ( params - > pen . usage_invalid & & ( field - > application = = HID_DG_PEN ) )
return - 1 ;
/* Let hid-core decide what to do */
return 0 ;
}
2019-02-10 13:13:50 +03:00
static int uclogic_input_configured ( struct hid_device * hdev ,
struct hid_input * hi )
{
2019-02-10 13:13:54 +03:00
struct uclogic_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
struct uclogic_params * params = & drvdata - > params ;
2019-02-10 13:13:50 +03:00
char * name ;
const char * suffix = NULL ;
struct hid_field * field ;
size_t len ;
2022-03-03 10:47:31 +03:00
size_t i ;
const struct uclogic_params_frame * frame ;
2019-02-10 13:13:50 +03:00
/* no report associated (HID_QUIRK_MULTI_INPUT not set) */
if ( ! hi - > report )
return 0 ;
2019-02-10 13:13:54 +03:00
/*
* If this is the input corresponding to the pen report
* in need of tweaking .
*/
if ( hi - > report - > id = = params - > pen . id ) {
/* Remember the input device so we can simulate events */
drvdata - > pen_input = hi - > input ;
}
2022-03-03 10:47:31 +03:00
/* If it's one of the frame devices */
for ( i = 0 ; i < ARRAY_SIZE ( params - > frame_list ) ; i + + ) {
frame = & params - > frame_list [ i ] ;
if ( hi - > report - > id = = frame - > id ) {
2022-03-03 10:47:32 +03:00
/* Assign custom suffix, if any */
suffix = frame - > suffix ;
2022-03-03 10:47:31 +03:00
/*
* Disable EV_MSC reports for touch ring interfaces to
* make the Wacom driver pickup touch ring extents
*/
2022-05-08 19:01:42 +03:00
if ( frame - > touch_byte > 0 )
2022-03-03 10:47:31 +03:00
__clear_bit ( EV_MSC , hi - > input - > evbit ) ;
}
}
2022-03-03 10:47:32 +03:00
if ( ! suffix ) {
field = hi - > report - > field [ 0 ] ;
2019-02-10 13:13:50 +03:00
2022-03-03 10:47:32 +03:00
switch ( field - > application ) {
case HID_GD_KEYBOARD :
suffix = " Keyboard " ;
break ;
case HID_GD_MOUSE :
suffix = " Mouse " ;
break ;
case HID_GD_KEYPAD :
suffix = " Pad " ;
break ;
case HID_DG_PEN :
2022-08-15 17:26:15 +03:00
case HID_DG_DIGITIZER :
2022-03-03 10:47:32 +03:00
suffix = " Pen " ;
break ;
case HID_CP_CONSUMER_CONTROL :
suffix = " Consumer Control " ;
break ;
case HID_GD_SYSTEM_CONTROL :
suffix = " System Control " ;
break ;
}
2019-02-10 13:13:50 +03:00
}
if ( suffix ) {
len = strlen ( hdev - > name ) + 2 + strlen ( suffix ) ;
name = devm_kzalloc ( & hi - > input - > dev , len , GFP_KERNEL ) ;
if ( name ) {
snprintf ( name , len , " %s %s " , hdev - > name , suffix ) ;
hi - > input - > name = name ;
}
}
return 0 ;
}
static int uclogic_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
int rc ;
2019-02-10 13:13:51 +03:00
struct uclogic_drvdata * drvdata = NULL ;
bool params_initialized = false ;
2019-02-10 13:13:50 +03:00
2021-12-01 21:35:03 +03:00
if ( ! hid_is_usb ( hdev ) )
return - EINVAL ;
2019-02-10 13:13:50 +03:00
/*
* libinput requires the pad interface to be on a different node
* than the pen , so use QUIRK_MULTI_INPUT for all tablets .
*/
hdev - > quirks | = HID_QUIRK_MULTI_INPUT ;
/* Allocate and assign driver data */
drvdata = devm_kzalloc ( & hdev - > dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
2019-02-10 13:13:51 +03:00
if ( drvdata = = NULL ) {
rc = - ENOMEM ;
goto failure ;
}
2019-02-10 13:13:54 +03:00
timer_setup ( & drvdata - > inrange_timer , uclogic_inrange_timeout , 0 ) ;
2019-02-10 13:14:04 +03:00
drvdata - > re_state = U8_MAX ;
2019-02-10 13:13:50 +03:00
hid_set_drvdata ( hdev , drvdata ) ;
2019-02-10 13:13:51 +03:00
/* Initialize the device and retrieve interface parameters */
rc = uclogic_params_init ( & drvdata - > params , hdev ) ;
if ( rc ! = 0 ) {
hid_err ( hdev , " failed probing parameters: %d \n " , rc ) ;
goto failure ;
}
params_initialized = true ;
2022-05-08 19:01:40 +03:00
hid_dbg ( hdev , " parameters: \n " ) ;
uclogic_params_hid_dbg ( hdev , & drvdata - > params ) ;
2019-02-10 13:13:51 +03:00
if ( drvdata - > params . invalid ) {
hid_info ( hdev , " interface is invalid, ignoring \n " ) ;
rc = - ENODEV ;
goto failure ;
}
2019-02-10 13:13:50 +03:00
2019-02-10 13:13:51 +03:00
/* Generate replacement report descriptor */
rc = uclogic_params_get_desc ( & drvdata - > params ,
& drvdata - > desc_ptr ,
& drvdata - > desc_size ) ;
if ( rc ) {
hid_err ( hdev ,
" failed generating replacement report descriptor: %d \n " ,
rc ) ;
goto failure ;
2019-02-10 13:13:50 +03:00
}
rc = hid_parse ( hdev ) ;
if ( rc ) {
hid_err ( hdev , " parse failed \n " ) ;
2019-02-10 13:13:51 +03:00
goto failure ;
2019-02-10 13:13:50 +03:00
}
rc = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( rc ) {
hid_err ( hdev , " hw start failed \n " ) ;
2019-02-10 13:13:51 +03:00
goto failure ;
2019-02-10 13:13:50 +03:00
}
return 0 ;
2019-02-10 13:13:51 +03:00
failure :
/* Assume "remove" might not be called if "probe" failed */
if ( params_initialized )
uclogic_params_cleanup ( & drvdata - > params ) ;
return rc ;
2019-02-10 13:13:50 +03:00
}
2019-02-10 13:13:52 +03:00
# ifdef CONFIG_PM
static int uclogic_resume ( struct hid_device * hdev )
{
int rc ;
struct uclogic_params params ;
/* Re-initialize the device, but discard parameters */
rc = uclogic_params_init ( & params , hdev ) ;
if ( rc ! = 0 )
hid_err ( hdev , " failed to re-initialize the device \n " ) ;
else
uclogic_params_cleanup ( & params ) ;
return rc ;
}
# endif
2022-02-10 22:04:34 +03:00
/**
* uclogic_raw_event_pen - handle raw pen events ( pen HID reports ) .
*
* @ drvdata : Driver data .
* @ data : Report data buffer , can be modified .
* @ size : Report data size , bytes .
*
* Returns :
* Negative value on error ( stops event delivery ) , zero for success .
*/
static int uclogic_raw_event_pen ( struct uclogic_drvdata * drvdata ,
u8 * data , int size )
{
2022-02-10 22:04:35 +03:00
struct uclogic_params_pen * pen = & drvdata - > params . pen ;
2022-02-10 22:04:34 +03:00
WARN_ON ( drvdata = = NULL ) ;
WARN_ON ( data = = NULL & & size ! = 0 ) ;
/* If in-range reports are inverted */
2022-02-10 22:04:35 +03:00
if ( pen - > inrange = =
2022-02-10 22:04:34 +03:00
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED ) {
/* Invert the in-range bit */
data [ 1 ] ^ = 0x40 ;
}
/*
* If report contains fragmented high - resolution pen
* coordinates
*/
2022-02-10 22:04:35 +03:00
if ( size > = 10 & & pen - > fragmented_hires ) {
2022-02-10 22:04:34 +03:00
u8 pressure_low_byte ;
u8 pressure_high_byte ;
/* Lift pressure bytes */
pressure_low_byte = data [ 6 ] ;
pressure_high_byte = data [ 7 ] ;
/*
* Move Y coord to make space for high - order X
* coord byte
*/
data [ 6 ] = data [ 5 ] ;
data [ 5 ] = data [ 4 ] ;
/* Move high-order X coord byte */
data [ 4 ] = data [ 8 ] ;
/* Move high-order Y coord byte */
data [ 7 ] = data [ 9 ] ;
/* Place pressure bytes */
data [ 8 ] = pressure_low_byte ;
data [ 9 ] = pressure_high_byte ;
}
/* If we need to emulate in-range detection */
2022-02-10 22:04:35 +03:00
if ( pen - > inrange = = UCLOGIC_PARAMS_PEN_INRANGE_NONE ) {
2022-02-10 22:04:34 +03:00
/* Set in-range bit */
data [ 1 ] | = 0x40 ;
/* (Re-)start in-range timeout */
mod_timer ( & drvdata - > inrange_timer ,
jiffies + msecs_to_jiffies ( 100 ) ) ;
}
/* If we report tilt and Y direction is flipped */
2022-02-10 22:04:35 +03:00
if ( size > = 12 & & pen - > tilt_y_flipped )
2022-02-10 22:04:34 +03:00
data [ 11 ] = - data [ 11 ] ;
return 0 ;
}
/**
* uclogic_raw_event_frame - handle raw frame events ( frame HID reports ) .
*
* @ drvdata : Driver data .
2022-02-19 13:01:57 +03:00
* @ frame : The parameters of the frame controls to handle .
2022-02-10 22:04:34 +03:00
* @ data : Report data buffer , can be modified .
* @ size : Report data size , bytes .
*
* Returns :
* Negative value on error ( stops event delivery ) , zero for success .
*/
2022-02-19 13:01:57 +03:00
static int uclogic_raw_event_frame (
struct uclogic_drvdata * drvdata ,
const struct uclogic_params_frame * frame ,
u8 * data , int size )
2022-02-10 22:04:34 +03:00
{
WARN_ON ( drvdata = = NULL ) ;
WARN_ON ( data = = NULL & & size ! = 0 ) ;
/* If need to, and can, set pad device ID for Wacom drivers */
2022-02-10 22:04:35 +03:00
if ( frame - > dev_id_byte > 0 & & frame - > dev_id_byte < size ) {
2022-03-03 10:47:31 +03:00
/* If we also have a touch ring and the finger left it */
2022-05-08 19:01:42 +03:00
if ( frame - > touch_byte > 0 & & frame - > touch_byte < size & &
data [ frame - > touch_byte ] = = 0 ) {
2022-03-03 10:47:31 +03:00
data [ frame - > dev_id_byte ] = 0 ;
} else {
data [ frame - > dev_id_byte ] = 0xf ;
}
2022-02-10 22:04:34 +03:00
}
2022-03-03 10:47:31 +03:00
2022-02-10 22:04:34 +03:00
/* If need to, and can, read rotary encoder state change */
2022-02-10 22:04:35 +03:00
if ( frame - > re_lsb > 0 & & frame - > re_lsb / 8 < size ) {
unsigned int byte = frame - > re_lsb / 8 ;
unsigned int bit = frame - > re_lsb % 8 ;
2022-02-10 22:04:34 +03:00
u8 change ;
u8 prev_state = drvdata - > re_state ;
/* Read Gray-coded state */
u8 state = ( data [ byte ] > > bit ) & 0x3 ;
/* Encode state change into 2-bit signed integer */
if ( ( prev_state = = 1 & & state = = 0 ) | |
( prev_state = = 2 & & state = = 3 ) ) {
change = 1 ;
} else if ( ( prev_state = = 2 & & state = = 0 ) | |
( prev_state = = 1 & & state = = 3 ) ) {
change = 3 ;
} else {
change = 0 ;
}
/* Write change */
data [ byte ] = ( data [ byte ] & ~ ( ( u8 ) 3 < < bit ) ) |
( change < < bit ) ;
/* Remember state */
drvdata - > re_state = state ;
}
2022-03-03 10:47:31 +03:00
/* If need to, and can, transform the touch ring reports */
2022-05-08 19:01:43 +03:00
if ( frame - > touch_byte > 0 & & frame - > touch_byte < size ) {
2022-05-08 19:01:42 +03:00
__s8 value = data [ frame - > touch_byte ] ;
2022-03-03 10:47:31 +03:00
2022-05-08 19:01:43 +03:00
if ( value ! = 0 ) {
if ( frame - > touch_flip_at ! = 0 ) {
value = frame - > touch_flip_at - value ;
if ( value < = 0 )
value = frame - > touch_max + value ;
}
data [ frame - > touch_byte ] = value - 1 ;
2022-03-03 10:47:31 +03:00
}
}
2022-04-14 14:09:35 +03:00
/* If need to, and can, transform the bitmap dial reports */
if ( frame - > bitmap_dial_byte > 0 & & frame - > bitmap_dial_byte < size ) {
if ( data [ frame - > bitmap_dial_byte ] = = 2 )
data [ frame - > bitmap_dial_byte ] = - 1 ;
}
2022-02-10 22:04:34 +03:00
return 0 ;
}
2019-02-10 13:13:51 +03:00
static int uclogic_raw_event ( struct hid_device * hdev ,
struct hid_report * report ,
u8 * data , int size )
2019-02-10 13:13:50 +03:00
{
2022-02-10 22:04:37 +03:00
unsigned int report_id = report - > id ;
2019-02-10 13:13:50 +03:00
struct uclogic_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
2019-02-10 13:13:51 +03:00
struct uclogic_params * params = & drvdata - > params ;
2022-02-19 13:01:50 +03:00
struct uclogic_params_pen_subreport * subreport ;
struct uclogic_params_pen_subreport * subreport_list_end ;
2022-02-19 13:01:57 +03:00
size_t i ;
2019-02-10 13:13:50 +03:00
2022-02-10 22:04:36 +03:00
/* Do not handle anything but input reports */
if ( report - > type ! = HID_INPUT_REPORT )
return 0 ;
2022-02-19 13:01:50 +03:00
while ( true ) {
/* Tweak pen reports, if necessary */
if ( ( report_id = = params - > pen . id ) & & ( size > = 2 ) ) {
subreport_list_end =
params - > pen . subreport_list +
ARRAY_SIZE ( params - > pen . subreport_list ) ;
/* Try to match a subreport */
for ( subreport = params - > pen . subreport_list ;
2022-02-19 13:01:51 +03:00
subreport < subreport_list_end ; subreport + + ) {
if ( subreport - > value ! = 0 & &
subreport - > value = = data [ 1 ] ) {
break ;
}
}
2022-02-19 13:01:50 +03:00
/* If a subreport matched */
if ( subreport < subreport_list_end ) {
/* Change to subreport ID, and restart */
report_id = data [ 0 ] = subreport - > id ;
continue ;
} else {
return uclogic_raw_event_pen ( drvdata , data , size ) ;
}
2019-02-10 13:13:51 +03:00
}
2019-02-10 13:13:50 +03:00
2022-02-19 13:01:50 +03:00
/* Tweak frame control reports, if necessary */
2022-02-19 13:01:57 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( params - > frame_list ) ; i + + ) {
if ( report_id = = params - > frame_list [ i ] . id ) {
return uclogic_raw_event_frame (
drvdata , & params - > frame_list [ i ] ,
data , size ) ;
}
}
2022-02-19 13:01:50 +03:00
break ;
}
2019-02-10 13:14:03 +03:00
2019-02-10 13:13:50 +03:00
return 0 ;
}
2019-02-10 13:13:51 +03:00
static void uclogic_remove ( struct hid_device * hdev )
{
struct uclogic_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
2019-02-10 13:13:54 +03:00
del_timer_sync ( & drvdata - > inrange_timer ) ;
2019-02-10 13:13:51 +03:00
hid_hw_stop ( hdev ) ;
kfree ( drvdata - > desc_ptr ) ;
uclogic_params_cleanup ( & drvdata - > params ) ;
}
2019-02-10 13:13:50 +03:00
static const struct hid_device_id uclogic_devices [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 ) } ,
2019-02-10 13:13:51 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_HUION ,
USB_DEVICE_ID_HUION_TABLET ) } ,
2019-06-16 02:15:06 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_HUION ,
2022-02-10 22:04:32 +03:00
USB_DEVICE_ID_HUION_TABLET2 ) } ,
2020-12-27 16:08:15 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_TRUST ,
USB_DEVICE_ID_TRUST_PANORA_TABLET ) } ,
2019-02-10 13:13:51 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_HUION_TABLET ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_YIYNOVA_TABLET ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45 ) } ,
2019-02-10 13:13:57 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47 ) } ,
2019-02-10 13:13:51 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGTIZER ,
USB_DEVICE_ID_UGTIZER_TABLET_GP0610 ) } ,
2020-10-16 09:38:05 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGTIZER ,
USB_DEVICE_ID_UGTIZER_TABLET_GT5040 ) } ,
2019-02-10 13:14:05 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_TABLET_G5 ) } ,
2019-02-10 13:13:51 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_TABLET_EX07S ) } ,
2019-06-08 12:23:24 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 ) } ,
2019-02-10 13:14:00 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 ) } ,
2019-02-10 13:14:01 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 ) } ,
2019-02-10 13:14:02 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 ) } ,
2022-06-11 14:39:14 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L ) } ,
2022-08-15 17:29:55 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S ) } ,
2022-05-08 19:01:45 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 ) } ,
2019-02-10 13:13:50 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( hid , uclogic_devices ) ;
static struct hid_driver uclogic_driver = {
. name = " uclogic " ,
. id_table = uclogic_devices ,
. probe = uclogic_probe ,
2019-02-10 13:13:51 +03:00
. remove = uclogic_remove ,
2019-02-10 13:13:50 +03:00
. report_fixup = uclogic_report_fixup ,
. raw_event = uclogic_raw_event ,
2022-04-21 20:50:51 +03:00
. input_mapping = uclogic_input_mapping ,
2019-02-10 13:13:50 +03:00
. input_configured = uclogic_input_configured ,
2019-02-10 13:13:52 +03:00
# ifdef CONFIG_PM
. resume = uclogic_resume ,
. reset_resume = uclogic_resume ,
# endif
2019-02-10 13:13:50 +03:00
} ;
module_hid_driver ( uclogic_driver ) ;
MODULE_AUTHOR ( " Martin Rusko " ) ;
MODULE_AUTHOR ( " Nikolai Kondrashov " ) ;
MODULE_LICENSE ( " GPL " ) ;