2019-02-10 12:13:51 +02:00
// SPDX-License-Identifier: GPL-2.0+
/*
* HID driver for UC - Logic devices not fully compliant with HID standard
* - tablet initialization and parameter retrieval
*
* Copyright ( c ) 2018 Nikolai Kondrashov
*/
/*
* 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 "hid-uclogic-params.h"
# include "hid-uclogic-rdesc.h"
# include "usbhid/usbhid.h"
# include "hid-ids.h"
# include <linux/ctype.h>
# include <asm/unaligned.h>
/**
2021-03-26 14:34:52 +00:00
* uclogic_params_pen_inrange_to_str ( ) - Convert a pen in - range reporting type
* to a string .
2019-02-10 12:13:51 +02:00
*
* @ inrange : The in - range reporting type to convert .
*
* Returns :
* The string representing the type , or NULL if the type is unknown .
*/
2022-05-08 18:01:40 +02:00
static const char * uclogic_params_pen_inrange_to_str (
enum uclogic_params_pen_inrange inrange )
2019-02-10 12:13:51 +02:00
{
switch ( inrange ) {
case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL :
return " normal " ;
case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED :
return " inverted " ;
2019-02-10 12:13:54 +02:00
case UCLOGIC_PARAMS_PEN_INRANGE_NONE :
return " none " ;
2019-02-10 12:13:51 +02:00
default :
return NULL ;
}
}
2022-05-08 18:01:40 +02:00
/**
* Dump tablet interface pen parameters with hid_dbg ( ) , indented with one tab .
*
* @ hdev : The HID device the pen parameters describe .
* @ pen : The pen parameters to dump .
*/
static void uclogic_params_pen_hid_dbg ( const struct hid_device * hdev ,
const struct uclogic_params_pen * pen )
{
size_t i ;
hid_dbg ( hdev , " \t .usage_invalid = %s \n " ,
( pen - > usage_invalid ? " true " : " false " ) ) ;
hid_dbg ( hdev , " \t .desc_ptr = %p \n " , pen - > desc_ptr ) ;
hid_dbg ( hdev , " \t .desc_size = %u \n " , pen - > desc_size ) ;
hid_dbg ( hdev , " \t .id = %u \n " , pen - > id ) ;
hid_dbg ( hdev , " \t .subreport_list = { \n " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pen - > subreport_list ) ; i + + ) {
hid_dbg ( hdev , " \t \t {0x%02hhx, %hhu}%s \n " ,
pen - > subreport_list [ i ] . value ,
pen - > subreport_list [ i ] . id ,
i < ( ARRAY_SIZE ( pen - > subreport_list ) - 1 ) ? " , " : " " ) ;
}
hid_dbg ( hdev , " \t } \n " ) ;
hid_dbg ( hdev , " \t .inrange = %s \n " ,
uclogic_params_pen_inrange_to_str ( pen - > inrange ) ) ;
hid_dbg ( hdev , " \t .fragmented_hires = %s \n " ,
( pen - > fragmented_hires ? " true " : " false " ) ) ;
hid_dbg ( hdev , " \t .tilt_y_flipped = %s \n " ,
( pen - > tilt_y_flipped ? " true " : " false " ) ) ;
}
/**
* Dump tablet interface frame parameters with hid_dbg ( ) , indented with two
* tabs .
*
* @ hdev : The HID device the pen parameters describe .
* @ frame : The frame parameters to dump .
*/
static void uclogic_params_frame_hid_dbg (
const struct hid_device * hdev ,
const struct uclogic_params_frame * frame )
{
hid_dbg ( hdev , " \t \t .desc_ptr = %p \n " , frame - > desc_ptr ) ;
hid_dbg ( hdev , " \t \t .desc_size = %u \n " , frame - > desc_size ) ;
hid_dbg ( hdev , " \t \t .id = %u \n " , frame - > id ) ;
hid_dbg ( hdev , " \t \t .suffix = %s \n " , frame - > suffix ) ;
hid_dbg ( hdev , " \t \t .re_lsb = %u \n " , frame - > re_lsb ) ;
hid_dbg ( hdev , " \t \t .dev_id_byte = %u \n " , frame - > dev_id_byte ) ;
hid_dbg ( hdev , " \t \t .touch_ring_byte = %u \n " , frame - > touch_ring_byte ) ;
hid_dbg ( hdev , " \t \t .touch_ring_max = %hhd \n " , frame - > touch_ring_max ) ;
hid_dbg ( hdev , " \t \t .touch_ring_flip_at = %hhd \n " ,
frame - > touch_ring_flip_at ) ;
hid_dbg ( hdev , " \t \t .bitmap_dial_byte = %u \n " ,
frame - > bitmap_dial_byte ) ;
}
/**
* Dump tablet interface parameters with hid_dbg ( ) .
*
* @ hdev : The HID device the parameters describe .
* @ params : The parameters to dump .
*/
void uclogic_params_hid_dbg ( const struct hid_device * hdev ,
const struct uclogic_params * params )
{
size_t i ;
hid_dbg ( hdev , " .invalid = %s \n " ,
params - > invalid ? " true " : " false " ) ;
hid_dbg ( hdev , " .desc_ptr = %p \n " , params - > desc_ptr ) ;
hid_dbg ( hdev , " .desc_size = %u \n " , params - > desc_size ) ;
hid_dbg ( hdev , " .pen = { \n " ) ;
uclogic_params_pen_hid_dbg ( hdev , & params - > pen ) ;
hid_dbg ( hdev , " \t } \n " ) ;
hid_dbg ( hdev , " .frame_list = { \n " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( params - > frame_list ) ; i + + ) {
hid_dbg ( hdev , " \t { \n " ) ;
uclogic_params_frame_hid_dbg ( hdev , & params - > frame_list [ i ] ) ;
hid_dbg ( hdev , " \t }%s \n " ,
i < ( ARRAY_SIZE ( params - > frame_list ) - 1 ) ? " , " : " " ) ;
}
hid_dbg ( hdev , " } \n " ) ;
}
2019-02-10 12:13:51 +02:00
/**
* uclogic_params_get_str_desc - retrieve a string descriptor from a HID
* device interface , putting it into a kmalloc - allocated buffer as is , without
* character encoding conversion .
*
* @ pbuf : Location for the kmalloc - allocated buffer pointer containing
* the retrieved descriptor . Not modified in case of error .
* Can be NULL to have retrieved descriptor discarded .
* @ hdev : The HID device of the tablet interface to retrieve the string
* descriptor from . Cannot be NULL .
* @ idx : Index of the string descriptor to request from the device .
* @ len : Length of the buffer to allocate and the data to retrieve .
*
* Returns :
* number of bytes retrieved ( < = len ) ,
* - EPIPE , if the descriptor was not found , or
* another negative errno code in case of other error .
*/
static int uclogic_params_get_str_desc ( __u8 * * pbuf , struct hid_device * hdev ,
__u8 idx , size_t len )
{
int rc ;
2022-01-05 18:29:13 +01:00
struct usb_device * udev ;
2019-02-10 12:13:51 +02:00
__u8 * buf = NULL ;
/* Check arguments */
if ( hdev = = NULL ) {
rc = - EINVAL ;
goto cleanup ;
}
2022-01-05 18:29:13 +01:00
udev = hid_to_usb_dev ( hdev ) ;
2019-02-10 12:13:51 +02:00
buf = kmalloc ( len , GFP_KERNEL ) ;
if ( buf = = NULL ) {
rc = - ENOMEM ;
goto cleanup ;
}
rc = usb_control_msg ( udev , usb_rcvctrlpipe ( udev , 0 ) ,
USB_REQ_GET_DESCRIPTOR , USB_DIR_IN ,
( USB_DT_STRING < < 8 ) + idx ,
0x0409 , buf , len ,
USB_CTRL_GET_TIMEOUT ) ;
if ( rc = = - EPIPE ) {
hid_dbg ( hdev , " string descriptor #%hhu not found \n " , idx ) ;
goto cleanup ;
} else if ( rc < 0 ) {
hid_err ( hdev ,
2020-12-15 06:54:54 -08:00
" failed retrieving string descriptor #%u: %d \n " ,
2019-02-10 12:13:51 +02:00
idx , rc ) ;
goto cleanup ;
}
if ( pbuf ! = NULL ) {
* pbuf = buf ;
buf = NULL ;
}
cleanup :
kfree ( buf ) ;
return rc ;
}
/**
* uclogic_params_pen_cleanup - free resources used by struct
* uclogic_params_pen ( tablet interface ' s pen input parameters ) .
* Can be called repeatedly .
*
* @ pen : Pen input parameters to cleanup . Cannot be NULL .
*/
static void uclogic_params_pen_cleanup ( struct uclogic_params_pen * pen )
{
kfree ( pen - > desc_ptr ) ;
memset ( pen , 0 , sizeof ( * pen ) ) ;
}
/**
2019-02-10 12:13:53 +02:00
* uclogic_params_pen_init_v1 ( ) - initialize tablet interface pen
* input and retrieve its parameters from the device , using v1 protocol .
2019-02-10 12:13:51 +02:00
*
* @ pen : Pointer to the pen parameters to initialize ( to be
* cleaned up with uclogic_params_pen_cleanup ( ) ) . Not modified in
* case of error , or if parameters are not found . Cannot be NULL .
* @ pfound : Location for a flag which is set to true if the parameters
* were found , and to false if not ( e . g . device was
* incompatible ) . Not modified in case of error . Cannot be NULL .
* @ hdev : The HID device of the tablet interface to initialize and get
* parameters from . Cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
2019-02-10 12:13:53 +02:00
static int uclogic_params_pen_init_v1 ( struct uclogic_params_pen * pen ,
bool * pfound ,
struct hid_device * hdev )
2019-02-10 12:13:51 +02:00
{
int rc ;
bool found = false ;
/* Buffer for (part of) the string descriptor */
__u8 * buf = NULL ;
/* Minimum descriptor length required, maximum seen so far is 18 */
const int len = 12 ;
s32 resolution ;
/* Pen report descriptor template parameters */
s32 desc_params [ UCLOGIC_RDESC_PEN_PH_ID_NUM ] ;
__u8 * desc_ptr = NULL ;
/* Check arguments */
if ( pen = = NULL | | pfound = = NULL | | hdev = = NULL ) {
rc = - EINVAL ;
goto cleanup ;
}
/*
* Read string descriptor containing pen input parameters .
* The specific string descriptor and data were discovered by sniffing
* the Windows driver traffic .
* NOTE : This enables fully - functional tablet mode .
*/
rc = uclogic_params_get_str_desc ( & buf , hdev , 100 , len ) ;
if ( rc = = - EPIPE ) {
hid_dbg ( hdev ,
" string descriptor with pen parameters not found, assuming not compatible \n " ) ;
goto finish ;
} else if ( rc < 0 ) {
hid_err ( hdev , " failed retrieving pen parameters: %d \n " , rc ) ;
goto cleanup ;
} else if ( rc ! = len ) {
hid_dbg ( hdev ,
" string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible \n " ,
rc , len ) ;
goto finish ;
}
/*
* Fill report descriptor parameters from the string descriptor
*/
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_LM ] =
get_unaligned_le16 ( buf + 2 ) ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_LM ] =
get_unaligned_le16 ( buf + 4 ) ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM ] =
get_unaligned_le16 ( buf + 8 ) ;
resolution = get_unaligned_le16 ( buf + 10 ) ;
if ( resolution = = 0 ) {
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_PM ] = 0 ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_PM ] = 0 ;
} else {
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_PM ] =
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_LM ] * 1000 /
resolution ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_PM ] =
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_LM ] * 1000 /
resolution ;
}
kfree ( buf ) ;
buf = NULL ;
/*
* Generate pen report descriptor
*/
desc_ptr = uclogic_rdesc_template_apply (
2022-02-19 11:01:55 +01:00
uclogic_rdesc_v1_pen_template_arr ,
uclogic_rdesc_v1_pen_template_size ,
2019-02-10 12:13:51 +02:00
desc_params , ARRAY_SIZE ( desc_params ) ) ;
if ( desc_ptr = = NULL ) {
rc = - ENOMEM ;
goto cleanup ;
}
/*
* Fill - in the parameters
*/
memset ( pen , 0 , sizeof ( * pen ) ) ;
pen - > desc_ptr = desc_ptr ;
desc_ptr = NULL ;
2022-02-19 11:01:55 +01:00
pen - > desc_size = uclogic_rdesc_v1_pen_template_size ;
pen - > id = UCLOGIC_RDESC_V1_PEN_ID ;
2019-02-10 12:13:51 +02:00
pen - > inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED ;
found = true ;
finish :
* pfound = found ;
rc = 0 ;
cleanup :
kfree ( desc_ptr ) ;
kfree ( buf ) ;
return rc ;
}
2019-02-10 12:13:56 +02:00
/**
* uclogic_params_get_le24 ( ) - get a 24 - bit little - endian number from a
* buffer .
*
* @ p : The pointer to the number buffer .
*
* Returns :
* The retrieved number
*/
static s32 uclogic_params_get_le24 ( const void * p )
{
const __u8 * b = p ;
return b [ 0 ] | ( b [ 1 ] < < 8UL ) | ( b [ 2 ] < < 16UL ) ;
}
/**
* uclogic_params_pen_init_v2 ( ) - initialize tablet interface pen
* input and retrieve its parameters from the device , using v2 protocol .
*
* @ pen : Pointer to the pen parameters to initialize ( to be
* cleaned up with uclogic_params_pen_cleanup ( ) ) . Not modified in
* case of error , or if parameters are not found . Cannot be NULL .
* @ pfound : Location for a flag which is set to true if the parameters
* were found , and to false if not ( e . g . device was
* incompatible ) . Not modified in case of error . Cannot be NULL .
* @ hdev : The HID device of the tablet interface to initialize and get
* parameters from . Cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_pen_init_v2 ( struct uclogic_params_pen * pen ,
bool * pfound ,
struct hid_device * hdev )
{
int rc ;
bool found = false ;
/* Buffer for (part of) the string descriptor */
__u8 * buf = NULL ;
/* Descriptor length required */
const int len = 18 ;
s32 resolution ;
/* Pen report descriptor template parameters */
s32 desc_params [ UCLOGIC_RDESC_PEN_PH_ID_NUM ] ;
__u8 * desc_ptr = NULL ;
/* Check arguments */
if ( pen = = NULL | | pfound = = NULL | | hdev = = NULL ) {
rc = - EINVAL ;
goto cleanup ;
}
/*
* Read string descriptor containing pen input parameters .
* The specific string descriptor and data were discovered by sniffing
* the Windows driver traffic .
* NOTE : This enables fully - functional tablet mode .
*/
rc = uclogic_params_get_str_desc ( & buf , hdev , 200 , len ) ;
if ( rc = = - EPIPE ) {
hid_dbg ( hdev ,
" string descriptor with pen parameters not found, assuming not compatible \n " ) ;
goto finish ;
} else if ( rc < 0 ) {
hid_err ( hdev , " failed retrieving pen parameters: %d \n " , rc ) ;
goto cleanup ;
} else if ( rc ! = len ) {
hid_dbg ( hdev ,
" string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible \n " ,
rc , len ) ;
goto finish ;
} else {
size_t i ;
/*
* Check it ' s not just a catch - all UTF - 16L E - encoded ASCII
* string ( such as the model name ) some tablets put into all
* unknown string descriptors .
*/
for ( i = 2 ;
i < len & &
( buf [ i ] > = 0x20 & & buf [ i ] < 0x7f & & buf [ i + 1 ] = = 0 ) ;
i + = 2 ) ;
if ( i > = len ) {
hid_dbg ( hdev ,
" string descriptor with pen parameters seems to contain only text, assuming not compatible \n " ) ;
goto finish ;
}
}
/*
* Fill report descriptor parameters from the string descriptor
*/
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_LM ] =
uclogic_params_get_le24 ( buf + 2 ) ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_LM ] =
uclogic_params_get_le24 ( buf + 5 ) ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM ] =
get_unaligned_le16 ( buf + 8 ) ;
resolution = get_unaligned_le16 ( buf + 10 ) ;
if ( resolution = = 0 ) {
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_PM ] = 0 ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_PM ] = 0 ;
} else {
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_PM ] =
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_LM ] * 1000 /
resolution ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_PM ] =
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_LM ] * 1000 /
resolution ;
}
kfree ( buf ) ;
buf = NULL ;
/*
* Generate pen report descriptor
*/
desc_ptr = uclogic_rdesc_template_apply (
2022-02-19 11:01:55 +01:00
uclogic_rdesc_v2_pen_template_arr ,
uclogic_rdesc_v2_pen_template_size ,
2019-02-10 12:13:56 +02:00
desc_params , ARRAY_SIZE ( desc_params ) ) ;
if ( desc_ptr = = NULL ) {
rc = - ENOMEM ;
goto cleanup ;
}
/*
* Fill - in the parameters
*/
memset ( pen , 0 , sizeof ( * pen ) ) ;
pen - > desc_ptr = desc_ptr ;
desc_ptr = NULL ;
2022-02-19 11:01:55 +01:00
pen - > desc_size = uclogic_rdesc_v2_pen_template_size ;
pen - > id = UCLOGIC_RDESC_V2_PEN_ID ;
2019-02-10 12:13:56 +02:00
pen - > inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE ;
pen - > fragmented_hires = true ;
2022-02-10 20:04:31 +01:00
pen - > tilt_y_flipped = true ;
2019-02-10 12:13:56 +02:00
found = true ;
finish :
* pfound = found ;
rc = 0 ;
cleanup :
kfree ( desc_ptr ) ;
kfree ( buf ) ;
return rc ;
}
2019-02-10 12:13:51 +02:00
/**
* uclogic_params_frame_cleanup - free resources used by struct
* uclogic_params_frame ( tablet interface ' s frame controls input parameters ) .
* Can be called repeatedly .
*
* @ frame : Frame controls input parameters to cleanup . Cannot be NULL .
*/
static void uclogic_params_frame_cleanup ( struct uclogic_params_frame * frame )
{
kfree ( frame - > desc_ptr ) ;
memset ( frame , 0 , sizeof ( * frame ) ) ;
}
/**
* uclogic_params_frame_init_with_desc ( ) - initialize tablet ' s frame control
* parameters with a static report descriptor .
*
* @ frame : Pointer to the frame parameters to initialize ( to be cleaned
* up with uclogic_params_frame_cleanup ( ) ) . Not modified in case
* of error . Cannot be NULL .
* @ desc_ptr : Report descriptor pointer . Can be NULL , if desc_size is zero .
* @ desc_size : Report descriptor size .
* @ id : Report ID used for frame reports , if they should be tweaked ,
* zero if not .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_frame_init_with_desc (
struct uclogic_params_frame * frame ,
const __u8 * desc_ptr ,
size_t desc_size ,
unsigned int id )
{
__u8 * copy_desc_ptr ;
if ( frame = = NULL | | ( desc_ptr = = NULL & & desc_size ! = 0 ) )
return - EINVAL ;
copy_desc_ptr = kmemdup ( desc_ptr , desc_size , GFP_KERNEL ) ;
if ( copy_desc_ptr = = NULL )
return - ENOMEM ;
memset ( frame , 0 , sizeof ( * frame ) ) ;
frame - > desc_ptr = copy_desc_ptr ;
frame - > desc_size = desc_size ;
frame - > id = id ;
return 0 ;
}
/**
2022-02-19 11:01:54 +01:00
* uclogic_params_frame_init_v1 ( ) - initialize v1 tablet interface frame
* controls .
2019-02-10 12:13:51 +02:00
*
* @ frame : Pointer to the frame parameters to initialize ( to be cleaned
* up with uclogic_params_frame_cleanup ( ) ) . Not modified in case
* of error , or if parameters are not found . Cannot be NULL .
* @ pfound : Location for a flag which is set to true if the parameters
* were found , and to false if not ( e . g . device was
* incompatible ) . Not modified in case of error . Cannot be NULL .
* @ hdev : The HID device of the tablet interface to initialize and get
* parameters from . Cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
2022-02-19 11:01:54 +01:00
static int uclogic_params_frame_init_v1 ( struct uclogic_params_frame * frame ,
2019-02-10 12:13:51 +02:00
bool * pfound ,
struct hid_device * hdev )
{
int rc ;
bool found = false ;
2022-01-05 18:29:15 +01:00
struct usb_device * usb_dev ;
2019-02-10 12:13:51 +02:00
char * str_buf = NULL ;
const size_t str_len = 16 ;
/* Check arguments */
if ( frame = = NULL | | pfound = = NULL | | hdev = = NULL ) {
rc = - EINVAL ;
goto cleanup ;
}
2022-01-05 18:29:15 +01:00
usb_dev = hid_to_usb_dev ( hdev ) ;
2019-02-10 12:13:51 +02:00
/*
* Enable generic button mode
*/
str_buf = kzalloc ( str_len , GFP_KERNEL ) ;
if ( str_buf = = NULL ) {
rc = - ENOMEM ;
goto cleanup ;
}
rc = usb_string ( usb_dev , 123 , str_buf , str_len ) ;
if ( rc = = - EPIPE ) {
hid_dbg ( hdev ,
" generic button -enabling string descriptor not found \n " ) ;
} else if ( rc < 0 ) {
goto cleanup ;
} else if ( strncmp ( str_buf , " HK On " , rc ) ! = 0 ) {
hid_dbg ( hdev ,
" invalid response to enabling generic buttons: \" %s \" \n " ,
str_buf ) ;
} else {
hid_dbg ( hdev , " generic buttons enabled \n " ) ;
rc = uclogic_params_frame_init_with_desc (
frame ,
2022-02-19 11:01:55 +01:00
uclogic_rdesc_v1_frame_arr ,
uclogic_rdesc_v1_frame_size ,
UCLOGIC_RDESC_V1_FRAME_ID ) ;
2019-02-10 12:13:51 +02:00
if ( rc ! = 0 )
goto cleanup ;
found = true ;
}
* pfound = found ;
rc = 0 ;
cleanup :
kfree ( str_buf ) ;
return rc ;
}
/**
* uclogic_params_cleanup - free resources used by struct uclogic_params
* ( tablet interface ' s parameters ) .
* Can be called repeatedly .
*
* @ params : Input parameters to cleanup . Cannot be NULL .
*/
void uclogic_params_cleanup ( struct uclogic_params * params )
{
if ( ! params - > invalid ) {
2022-02-19 11:01:57 +01:00
size_t i ;
2019-02-10 12:13:51 +02:00
kfree ( params - > desc_ptr ) ;
2022-02-19 11:01:49 +01:00
uclogic_params_pen_cleanup ( & params - > pen ) ;
2022-02-19 11:01:57 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( params - > frame_list ) ; i + + )
uclogic_params_frame_cleanup ( & params - > frame_list [ i ] ) ;
2019-02-10 12:13:51 +02:00
memset ( params , 0 , sizeof ( * params ) ) ;
}
}
/**
2021-03-26 14:34:52 +00:00
* uclogic_params_get_desc ( ) - Get a replacement report descriptor for a
* tablet ' s interface .
2019-02-10 12:13:51 +02:00
*
* @ params : The parameters of a tablet interface to get report
* descriptor for . Cannot be NULL .
* @ pdesc : Location for the resulting , kmalloc - allocated report
* descriptor pointer , or for NULL , if there ' s no replacement
* report descriptor . Not modified in case of error . Cannot be
* NULL .
* @ psize : Location for the resulting report descriptor size , not set if
* there ' s no replacement report descriptor . Not modified in case
* of error . Cannot be NULL .
*
* Returns :
* Zero , if successful .
* - EINVAL , if invalid arguments are supplied .
* - ENOMEM , if failed to allocate memory .
*/
int uclogic_params_get_desc ( const struct uclogic_params * params ,
__u8 * * pdesc ,
unsigned int * psize )
{
2022-02-19 11:01:57 +01:00
int rc = - ENOMEM ;
bool present = false ;
unsigned int size = 0 ;
2019-02-10 12:13:51 +02:00
__u8 * desc = NULL ;
2022-02-19 11:01:57 +01:00
size_t i ;
2019-02-10 12:13:51 +02:00
/* Check arguments */
if ( params = = NULL | | pdesc = = NULL | | psize = = NULL )
return - EINVAL ;
2022-02-19 11:01:57 +01:00
/* Concatenate descriptors */
# define ADD_DESC(_desc_ptr, _desc_size) \
do { \
unsigned int new_size ; \
__u8 * new_desc ; \
if ( ( _desc_ptr ) = = NULL ) { \
break ; \
} \
new_size = size + ( _desc_size ) ; \
new_desc = krealloc ( desc , new_size , GFP_KERNEL ) ; \
if ( new_desc = = NULL ) { \
goto cleanup ; \
} \
memcpy ( new_desc + size , ( _desc_ptr ) , ( _desc_size ) ) ; \
desc = new_desc ; \
size = new_size ; \
present = true ; \
} while ( 0 )
ADD_DESC ( params - > desc_ptr , params - > desc_size ) ;
ADD_DESC ( params - > pen . desc_ptr , params - > pen . desc_size ) ;
for ( i = 0 ; i < ARRAY_SIZE ( params - > frame_list ) ; i + + ) {
ADD_DESC ( params - > frame_list [ i ] . desc_ptr ,
params - > frame_list [ i ] . desc_size ) ;
}
2019-02-10 12:13:51 +02:00
2022-02-19 11:01:57 +01:00
# undef ADD_DESC
2019-02-10 12:13:51 +02:00
2022-02-19 11:01:57 +01:00
if ( present ) {
* pdesc = desc ;
2019-02-10 12:13:51 +02:00
* psize = size ;
2022-02-19 11:01:57 +01:00
desc = NULL ;
2019-02-10 12:13:51 +02:00
}
2022-02-19 11:01:57 +01:00
rc = 0 ;
cleanup :
kfree ( desc ) ;
return rc ;
2019-02-10 12:13:51 +02:00
}
/**
* uclogic_params_init_invalid ( ) - initialize tablet interface parameters ,
* specifying the interface is invalid .
*
* @ params : Parameters to initialize ( to be cleaned with
* uclogic_params_cleanup ( ) ) . Cannot be NULL .
*/
static void uclogic_params_init_invalid ( struct uclogic_params * params )
{
params - > invalid = true ;
}
/**
* uclogic_params_init_with_opt_desc ( ) - initialize tablet interface
* parameters with an optional replacement report descriptor . Only modify
* report descriptor , if the original report descriptor matches the expected
* size .
*
* @ params : Parameters to initialize ( to be cleaned with
* uclogic_params_cleanup ( ) ) . Not modified in case of
* error . Cannot be NULL .
* @ hdev : The HID device of the tablet interface create the
* parameters for . Cannot be NULL .
* @ orig_desc_size : Expected size of the original report descriptor to
* be replaced .
* @ desc_ptr : Pointer to the replacement report descriptor .
* Can be NULL , if desc_size is zero .
* @ desc_size : Size of the replacement report descriptor .
*
* Returns :
* Zero , if successful . - EINVAL if an invalid argument was passed .
* - ENOMEM , if failed to allocate memory .
*/
static int uclogic_params_init_with_opt_desc ( struct uclogic_params * params ,
struct hid_device * hdev ,
unsigned int orig_desc_size ,
__u8 * desc_ptr ,
unsigned int desc_size )
{
__u8 * desc_copy_ptr = NULL ;
unsigned int desc_copy_size ;
int rc ;
/* Check arguments */
if ( params = = NULL | | hdev = = NULL | |
( desc_ptr = = NULL & & desc_size ! = 0 ) ) {
rc = - EINVAL ;
goto cleanup ;
}
/* Replace report descriptor, if it matches */
if ( hdev - > dev_rsize = = orig_desc_size ) {
hid_dbg ( hdev ,
" device report descriptor matches the expected size, replacing \n " ) ;
desc_copy_ptr = kmemdup ( desc_ptr , desc_size , GFP_KERNEL ) ;
if ( desc_copy_ptr = = NULL ) {
rc = - ENOMEM ;
goto cleanup ;
}
desc_copy_size = desc_size ;
} else {
hid_dbg ( hdev ,
" device report descriptor doesn't match the expected size (%u != %u), preserving \n " ,
hdev - > dev_rsize , orig_desc_size ) ;
desc_copy_ptr = NULL ;
desc_copy_size = 0 ;
}
/* Output parameters */
memset ( params , 0 , sizeof ( * params ) ) ;
params - > desc_ptr = desc_copy_ptr ;
desc_copy_ptr = NULL ;
params - > desc_size = desc_copy_size ;
rc = 0 ;
cleanup :
kfree ( desc_copy_ptr ) ;
return rc ;
}
/**
2021-03-26 14:34:52 +00:00
* uclogic_params_huion_init ( ) - initialize a Huion tablet interface and discover
2019-02-10 12:13:51 +02:00
* its parameters .
*
* @ params : Parameters to fill in ( to be cleaned with
* uclogic_params_cleanup ( ) ) . Not modified in case of error .
* Cannot be NULL .
* @ hdev : The HID device of the tablet interface to initialize and get
* parameters from . Cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_huion_init ( struct uclogic_params * params ,
struct hid_device * hdev )
{
int rc ;
2022-01-05 18:29:14 +01:00
struct usb_device * udev ;
struct usb_interface * iface ;
__u8 bInterfaceNumber ;
2019-02-10 12:13:51 +02:00
bool found ;
/* The resulting parameters (noop) */
struct uclogic_params p = { 0 , } ;
2019-02-10 12:13:56 +02:00
static const char transition_ver [ ] = " HUION_T153_160607 " ;
char * ver_ptr = NULL ;
const size_t ver_len = sizeof ( transition_ver ) + 1 ;
2019-02-10 12:13:51 +02:00
/* Check arguments */
if ( params = = NULL | | hdev = = NULL ) {
rc = - EINVAL ;
goto cleanup ;
}
2022-01-05 18:29:14 +01:00
udev = hid_to_usb_dev ( hdev ) ;
iface = to_usb_interface ( hdev - > dev . parent ) ;
bInterfaceNumber = iface - > cur_altsetting - > desc . bInterfaceNumber ;
2022-04-21 19:50:50 +02:00
/* If it's a custom keyboard interface */
if ( bInterfaceNumber = = 1 ) {
2022-04-21 19:50:52 +02:00
/* Keep everything intact, but mark pen usage invalid */
p . pen . usage_invalid = true ;
2022-04-21 19:50:50 +02:00
goto output ;
/* Else, if it's not a pen interface */
} else if ( bInterfaceNumber ! = 0 ) {
2022-02-19 11:01:49 +01:00
uclogic_params_init_invalid ( & p ) ;
2019-02-10 12:13:51 +02:00
goto output ;
}
2019-02-10 12:13:56 +02:00
/* Try to get firmware version */
ver_ptr = kzalloc ( ver_len , GFP_KERNEL ) ;
if ( ver_ptr = = NULL ) {
rc = - ENOMEM ;
goto cleanup ;
}
rc = usb_string ( udev , 201 , ver_ptr , ver_len ) ;
if ( rc = = - EPIPE ) {
* ver_ptr = ' \0 ' ;
} else if ( rc < 0 ) {
hid_err ( hdev ,
" failed retrieving Huion firmware version: %d \n " , rc ) ;
goto cleanup ;
}
/* If this is a transition firmware */
if ( strcmp ( ver_ptr , transition_ver ) = = 0 ) {
hid_dbg ( hdev ,
" transition firmware detected, not probing pen v2 parameters \n " ) ;
} else {
/* Try to probe v2 pen parameters */
rc = uclogic_params_pen_init_v2 ( & p . pen , & found , hdev ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
" failed probing pen v2 parameters: %d \n " , rc ) ;
goto cleanup ;
} else if ( found ) {
hid_dbg ( hdev , " pen v2 parameters found \n " ) ;
2022-03-03 08:47:34 +01:00
/* Create v2 frame button parameters */
2019-02-10 12:13:56 +02:00
rc = uclogic_params_frame_init_with_desc (
2022-02-19 11:01:57 +01:00
& p . frame_list [ 0 ] ,
2022-03-03 08:47:34 +01:00
uclogic_rdesc_v2_frame_buttons_arr ,
uclogic_rdesc_v2_frame_buttons_size ,
UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID ) ;
2019-02-10 12:13:56 +02:00
if ( rc ! = 0 ) {
hid_err ( hdev ,
2022-03-03 08:47:34 +01:00
" failed creating v2 frame button parameters: %d \n " ,
2019-02-10 12:13:56 +02:00
rc ) ;
goto cleanup ;
}
2022-03-03 08:47:34 +01:00
/* Create v2 frame touch ring parameters */
rc = uclogic_params_frame_init_with_desc (
& p . frame_list [ 1 ] ,
uclogic_rdesc_v2_frame_touch_ring_arr ,
uclogic_rdesc_v2_frame_touch_ring_size ,
UCLOGIC_RDESC_V2_FRAME_TOUCH_RING_ID ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
" failed creating v2 frame touch ring parameters: %d \n " ,
rc ) ;
goto cleanup ;
}
p . frame_list [ 1 ] . suffix = " Touch Ring " ;
p . frame_list [ 1 ] . dev_id_byte =
UCLOGIC_RDESC_V2_FRAME_TOUCH_RING_DEV_ID_BYTE ;
p . frame_list [ 1 ] . touch_ring_byte = 5 ;
p . frame_list [ 1 ] . touch_ring_max = 12 ;
p . frame_list [ 1 ] . touch_ring_flip_at = 6 ;
2022-04-14 13:09:36 +02:00
/* Create v2 frame dial parameters */
rc = uclogic_params_frame_init_with_desc (
& p . frame_list [ 2 ] ,
uclogic_rdesc_v2_frame_dial_arr ,
uclogic_rdesc_v2_frame_dial_size ,
UCLOGIC_RDESC_V2_FRAME_DIAL_ID ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
" failed creating v2 frame dial parameters: %d \n " ,
rc ) ;
goto cleanup ;
}
p . frame_list [ 2 ] . suffix = " Dial " ;
p . frame_list [ 2 ] . dev_id_byte =
UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE ;
p . frame_list [ 2 ] . bitmap_dial_byte = 5 ;
2022-03-03 08:47:34 +01:00
/*
* Link button and touch ring subreports from pen
* reports
*/
2022-02-19 11:01:51 +01:00
p . pen . subreport_list [ 0 ] . value = 0xe0 ;
2022-02-19 11:01:50 +01:00
p . pen . subreport_list [ 0 ] . id =
2022-03-03 08:47:34 +01:00
UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID ;
p . pen . subreport_list [ 1 ] . value = 0xf0 ;
p . pen . subreport_list [ 1 ] . id =
UCLOGIC_RDESC_V2_FRAME_TOUCH_RING_ID ;
2022-04-14 13:09:36 +02:00
p . pen . subreport_list [ 2 ] . value = 0xf1 ;
p . pen . subreport_list [ 2 ] . id =
UCLOGIC_RDESC_V2_FRAME_DIAL_ID ;
2019-02-10 12:13:56 +02:00
goto output ;
}
hid_dbg ( hdev , " pen v2 parameters not found \n " ) ;
}
2019-02-10 12:13:53 +02:00
/* Try to probe v1 pen parameters */
rc = uclogic_params_pen_init_v1 ( & p . pen , & found , hdev ) ;
2019-02-10 12:13:51 +02:00
if ( rc ! = 0 ) {
hid_err ( hdev ,
2019-02-10 12:13:53 +02:00
" failed probing pen v1 parameters: %d \n " , rc ) ;
2019-02-10 12:13:51 +02:00
goto cleanup ;
} else if ( found ) {
2019-02-10 12:13:53 +02:00
hid_dbg ( hdev , " pen v1 parameters found \n " ) ;
2022-02-19 11:01:54 +01:00
/* Try to probe v1 frame */
2022-02-19 11:01:57 +01:00
rc = uclogic_params_frame_init_v1 ( & p . frame_list [ 0 ] ,
2022-02-19 11:01:54 +01:00
& found , hdev ) ;
2019-02-10 12:13:51 +02:00
if ( rc ! = 0 ) {
2022-02-19 11:01:54 +01:00
hid_err ( hdev , " v1 frame probing failed: %d \n " , rc ) ;
2019-02-10 12:13:51 +02:00
goto cleanup ;
}
2022-02-19 11:01:54 +01:00
hid_dbg ( hdev , " frame v1 parameters%s found \n " ,
2019-02-10 12:13:51 +02:00
( found ? " " : " not " ) ) ;
if ( found ) {
2022-02-19 11:01:50 +01:00
/* Link frame button subreports from pen reports */
2022-02-19 11:01:51 +01:00
p . pen . subreport_list [ 0 ] . value = 0xe0 ;
2022-02-19 11:01:50 +01:00
p . pen . subreport_list [ 0 ] . id =
2022-02-19 11:01:55 +01:00
UCLOGIC_RDESC_V1_FRAME_ID ;
2019-02-10 12:13:51 +02:00
}
goto output ;
}
2019-02-10 12:13:53 +02:00
hid_dbg ( hdev , " pen v1 parameters not found \n " ) ;
2019-02-10 12:13:51 +02:00
uclogic_params_init_invalid ( & p ) ;
output :
/* Output parameters */
memcpy ( params , & p , sizeof ( * params ) ) ;
memset ( & p , 0 , sizeof ( p ) ) ;
rc = 0 ;
cleanup :
2019-02-10 12:13:56 +02:00
kfree ( ver_ptr ) ;
2019-02-10 12:13:51 +02:00
uclogic_params_cleanup ( & p ) ;
return rc ;
}
/**
* uclogic_params_init ( ) - initialize a tablet interface and discover its
* parameters .
*
* @ params : Parameters to fill in ( to be cleaned with
* uclogic_params_cleanup ( ) ) . Not modified in case of error .
* Cannot be NULL .
* @ hdev : The HID device of the tablet interface to initialize and get
2019-02-10 12:14:06 +02:00
* parameters from . Cannot be NULL . Must be using the USB low - level
* driver , i . e . be an actual USB tablet .
2019-02-10 12:13:51 +02:00
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
int uclogic_params_init ( struct uclogic_params * params ,
struct hid_device * hdev )
{
int rc ;
2022-01-05 18:29:12 +01:00
struct usb_device * udev ;
__u8 bNumInterfaces ;
struct usb_interface * iface ;
__u8 bInterfaceNumber ;
2019-02-10 12:13:51 +02:00
bool found ;
/* The resulting parameters (noop) */
struct uclogic_params p = { 0 , } ;
/* Check arguments */
2021-12-01 19:35:01 +01:00
if ( params = = NULL | | hdev = = NULL | | ! hid_is_usb ( hdev ) ) {
2019-02-10 12:13:51 +02:00
rc = - EINVAL ;
goto cleanup ;
}
2022-01-05 18:29:12 +01:00
udev = hid_to_usb_dev ( hdev ) ;
bNumInterfaces = udev - > config - > desc . bNumInterfaces ;
iface = to_usb_interface ( hdev - > dev . parent ) ;
bInterfaceNumber = iface - > cur_altsetting - > desc . bInterfaceNumber ;
2019-02-10 12:13:51 +02:00
/*
* Set replacement report descriptor if the original matches the
* specified size . Otherwise keep interface unchanged .
*/
# define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
uclogic_params_init_with_opt_desc ( \
& p , hdev , \
UCLOGIC_RDESC_ # # _orig_desc_token # # _SIZE , \
uclogic_rdesc_ # # _new_desc_token # # _arr , \
uclogic_rdesc_ # # _new_desc_token # # _size )
# define VID_PID(_vid, _pid) \
( ( ( __u32 ) ( _vid ) < < 16 ) | ( ( __u32 ) ( _pid ) & U16_MAX ) )
/*
* Handle specific interfaces for specific tablets .
*
* Observe the following logic :
*
* If the interface is recognized as producing certain useful input :
* Mark interface as valid .
* Output interface parameters .
* Else , if the interface is recognized as * not * producing any useful
* input :
* Mark interface as invalid .
* Else :
* Mark interface as valid .
* Output noop parameters .
*
* Rule of thumb : it is better to disable a broken interface than let
* it spew garbage input .
*/
switch ( VID_PID ( hdev - > vendor , hdev - > product ) ) {
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 ) :
rc = WITH_OPT_DESC ( PF1209_ORIG , pf1209_fixed ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U ) :
rc = WITH_OPT_DESC ( WPXXXXU_ORIG , wp4030u_fixed ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U ) :
2019-02-10 12:13:58 +02:00
if ( hdev - > dev_rsize = = UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE ) {
if ( bInterfaceNumber = = 0 ) {
/* Try to probe v1 pen parameters */
rc = uclogic_params_pen_init_v1 ( & p . pen ,
& found , hdev ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
" pen probing failed: %d \n " ,
rc ) ;
goto cleanup ;
}
if ( ! found ) {
hid_warn ( hdev ,
" pen parameters not found " ) ;
}
} else {
uclogic_params_init_invalid ( & p ) ;
}
} else {
rc = WITH_OPT_DESC ( WPXXXXU_ORIG , wp5540u_fixed ) ;
if ( rc ! = 0 )
goto cleanup ;
}
2019-02-10 12:13:51 +02:00
break ;
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U ) :
rc = WITH_OPT_DESC ( WPXXXXU_ORIG , wp8060u_fixed ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 ) :
rc = WITH_OPT_DESC ( WP1062_ORIG , wp1062_fixed ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 ) :
switch ( bInterfaceNumber ) {
case 0 :
rc = WITH_OPT_DESC ( TWHL850_ORIG0 , twhl850_fixed0 ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case 1 :
rc = WITH_OPT_DESC ( TWHL850_ORIG1 , twhl850_fixed1 ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case 2 :
rc = WITH_OPT_DESC ( TWHL850_ORIG2 , twhl850_fixed2 ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
}
break ;
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 ) :
/*
* If it is not a three - interface version , which is known to
* respond to initialization .
*/
if ( bNumInterfaces ! = 3 ) {
switch ( bInterfaceNumber ) {
case 0 :
rc = WITH_OPT_DESC ( TWHA60_ORIG0 ,
twha60_fixed0 ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case 1 :
rc = WITH_OPT_DESC ( TWHA60_ORIG1 ,
twha60_fixed1 ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
}
break ;
}
2020-08-23 17:36:59 -05:00
fallthrough ;
2019-02-10 12:13:51 +02:00
case VID_PID ( USB_VENDOR_ID_HUION ,
USB_DEVICE_ID_HUION_TABLET ) :
2019-06-15 18:15:06 -05:00
case VID_PID ( USB_VENDOR_ID_HUION ,
2022-02-10 20:04:32 +01:00
USB_DEVICE_ID_HUION_TABLET2 ) :
2019-02-10 12:13:51 +02:00
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_HUION_TABLET ) :
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_YIYNOVA_TABLET ) :
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81 ) :
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 ) :
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45 ) :
2019-02-10 12:13:57 +02:00
case VID_PID ( USB_VENDOR_ID_UCLOGIC ,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47 ) :
2019-02-10 12:13:51 +02:00
rc = uclogic_params_huion_init ( & p , hdev ) ;
if ( rc ! = 0 )
goto cleanup ;
break ;
case VID_PID ( USB_VENDOR_ID_UGTIZER ,
USB_DEVICE_ID_UGTIZER_TABLET_GP0610 ) :
2020-10-16 08:38:05 +02:00
case VID_PID ( USB_VENDOR_ID_UGTIZER ,
USB_DEVICE_ID_UGTIZER_TABLET_GT5040 ) :
2019-02-10 12:14:00 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 ) :
2019-02-10 12:14:01 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 ) :
2019-06-08 17:23:24 +08:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 ) :
2019-02-10 12:13:51 +02:00
/* If this is the pen interface */
if ( bInterfaceNumber = = 1 ) {
2019-02-10 12:13:53 +02:00
/* Probe v1 pen parameters */
rc = uclogic_params_pen_init_v1 ( & p . pen , & found , hdev ) ;
2019-02-10 12:13:51 +02:00
if ( rc ! = 0 ) {
hid_err ( hdev , " pen probing failed: %d \n " , rc ) ;
goto cleanup ;
}
if ( ! found ) {
hid_warn ( hdev , " pen parameters not found " ) ;
uclogic_params_init_invalid ( & p ) ;
}
} else {
2022-02-19 11:01:49 +01:00
uclogic_params_init_invalid ( & p ) ;
2019-02-10 12:14:02 +02:00
}
break ;
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 ) :
/* If this is the pen and frame interface */
if ( bInterfaceNumber = = 1 ) {
/* Probe v1 pen parameters */
rc = uclogic_params_pen_init_v1 ( & p . pen , & found , hdev ) ;
if ( rc ! = 0 ) {
hid_err ( hdev , " pen probing failed: %d \n " , rc ) ;
goto cleanup ;
}
/* Initialize frame parameters */
rc = uclogic_params_frame_init_with_desc (
2022-02-19 11:01:57 +01:00
& p . frame_list [ 0 ] ,
2019-02-10 12:14:02 +02:00
uclogic_rdesc_xppen_deco01_frame_arr ,
uclogic_rdesc_xppen_deco01_frame_size ,
0 ) ;
if ( rc ! = 0 )
goto cleanup ;
} else {
2022-02-19 11:01:49 +01:00
uclogic_params_init_invalid ( & p ) ;
2019-02-10 12:13:51 +02:00
}
2019-02-10 12:14:05 +02:00
break ;
2020-12-27 14:08:15 +01:00
case VID_PID ( USB_VENDOR_ID_TRUST ,
USB_DEVICE_ID_TRUST_PANORA_TABLET ) :
2019-02-10 12:14:05 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_TABLET_G5 ) :
/* Ignore non-pen interfaces */
if ( bInterfaceNumber ! = 1 ) {
uclogic_params_init_invalid ( & p ) ;
break ;
}
rc = uclogic_params_pen_init_v1 ( & p . pen , & found , hdev ) ;
if ( rc ! = 0 ) {
hid_err ( hdev , " pen probing failed: %d \n " , rc ) ;
goto cleanup ;
} else if ( found ) {
rc = uclogic_params_frame_init_with_desc (
2022-02-19 11:01:57 +01:00
& p . frame_list [ 0 ] ,
2019-02-10 12:14:05 +02:00
uclogic_rdesc_ugee_g5_frame_arr ,
uclogic_rdesc_ugee_g5_frame_size ,
UCLOGIC_RDESC_UGEE_G5_FRAME_ID ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
2022-02-19 11:01:54 +01:00
" failed creating frame parameters: %d \n " ,
2019-02-10 12:14:05 +02:00
rc ) ;
goto cleanup ;
}
2022-02-19 11:01:57 +01:00
p . frame_list [ 0 ] . re_lsb =
2019-02-10 12:14:05 +02:00
UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB ;
2022-02-19 11:01:57 +01:00
p . frame_list [ 0 ] . dev_id_byte =
2019-02-10 12:14:05 +02:00
UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE ;
} else {
hid_warn ( hdev , " pen parameters not found " ) ;
uclogic_params_init_invalid ( & p ) ;
}
2019-02-10 12:13:59 +02:00
break ;
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_TABLET_EX07S ) :
/* Ignore non-pen interfaces */
if ( bInterfaceNumber ! = 1 ) {
uclogic_params_init_invalid ( & p ) ;
break ;
}
rc = uclogic_params_pen_init_v1 ( & p . pen , & found , hdev ) ;
if ( rc ! = 0 ) {
hid_err ( hdev , " pen probing failed: %d \n " , rc ) ;
goto cleanup ;
} else if ( found ) {
rc = uclogic_params_frame_init_with_desc (
2022-02-19 11:01:57 +01:00
& p . frame_list [ 0 ] ,
2022-02-19 11:01:54 +01:00
uclogic_rdesc_ugee_ex07_frame_arr ,
uclogic_rdesc_ugee_ex07_frame_size ,
2019-02-10 12:13:59 +02:00
0 ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
2022-02-19 11:01:54 +01:00
" failed creating frame parameters: %d \n " ,
2019-02-10 12:13:59 +02:00
rc ) ;
goto cleanup ;
}
} else {
hid_warn ( hdev , " pen parameters not found " ) ;
uclogic_params_init_invalid ( & p ) ;
}
2019-02-10 12:13:51 +02:00
break ;
}
# undef VID_PID
# undef WITH_OPT_DESC
/* Output parameters */
memcpy ( params , & p , sizeof ( * params ) ) ;
memset ( & p , 0 , sizeof ( p ) ) ;
rc = 0 ;
cleanup :
uclogic_params_cleanup ( & p ) ;
return rc ;
}