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>
2022-10-29 18:12:40 +02:00
# include <linux/string.h>
2019-02-10 12:13:51 +02:00
# 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 .
*
2022-06-04 09:07:11 +07:00
* Return :
* * The string representing the type , or
* * % NULL if the type is unknown .
2019-02-10 12:13:51 +02:00
*/
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
/**
2022-06-04 09:07:11 +07:00
* uclogic_params_pen_hid_dbg ( ) - Dump tablet interface pen parameters
2022-05-08 18:01:40 +02:00
* @ hdev : The HID device the pen parameters describe .
* @ pen : The pen parameters to dump .
2022-06-04 09:07:11 +07:00
*
* Dump tablet interface pen parameters with hid_dbg ( ) . The dump is indented
* with a tab .
2022-05-08 18:01:40 +02:00
*/
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 " ) ) ;
}
/**
2022-06-04 09:07:11 +07:00
* uclogic_params_frame_hid_dbg ( ) - Dump tablet interface frame parameters
2022-05-08 18:01:40 +02:00
* @ hdev : The HID device the pen parameters describe .
* @ frame : The frame parameters to dump .
2022-06-04 09:07:11 +07:00
*
* Dump tablet interface frame parameters with hid_dbg ( ) . The dump is
* indented with two tabs .
2022-05-08 18:01:40 +02:00
*/
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 ) ;
2022-05-08 18:01:42 +02:00
hid_dbg ( hdev , " \t \t .touch_byte = %u \n " , frame - > touch_byte ) ;
hid_dbg ( hdev , " \t \t .touch_max = %hhd \n " , frame - > touch_max ) ;
hid_dbg ( hdev , " \t \t .touch_flip_at = %hhd \n " ,
frame - > touch_flip_at ) ;
2022-05-08 18:01:40 +02:00
hid_dbg ( hdev , " \t \t .bitmap_dial_byte = %u \n " ,
frame - > bitmap_dial_byte ) ;
}
/**
2022-06-04 09:07:11 +07:00
* uclogic_params_hid_dbg ( ) - Dump tablet interface parameters
2022-05-08 18:01:40 +02:00
* @ hdev : The HID device the parameters describe .
* @ params : The parameters to dump .
2022-06-04 09:07:11 +07:00
*
* Dump tablet interface parameters with hid_dbg ( ) .
2022-05-08 18:01:40 +02:00
*/
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 */
2022-06-11 13:39:11 +02:00
s32 desc_params [ UCLOGIC_RDESC_PH_ID_NUM ] ;
2019-02-10 12:13:51 +02:00
__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 .
*
2022-05-08 18:01:41 +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 .
* @ pparams_ptr : Location for a kmalloc ' ed pointer to the retrieved raw
* parameters , which could be used to identify the tablet
* to some extent . Should be freed with kfree after use .
* NULL , if not needed . Not modified in case of error .
* Only set if * pfound is set to true .
* @ pparams_len : Location for the length of the retrieved raw
* parameters . NULL , if not needed . Not modified in case
* of error . Only set if * pfound is set to true .
* @ hdev : The HID device of the tablet interface to initialize
* and get parameters from . Cannot be NULL .
2019-02-10 12:13:56 +02:00
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_pen_init_v2 ( struct uclogic_params_pen * pen ,
bool * pfound ,
2022-05-08 18:01:41 +02:00
__u8 * * pparams_ptr ,
size_t * pparams_len ,
2019-02-10 12:13:56 +02:00
struct hid_device * hdev )
{
int rc ;
bool found = false ;
2022-05-08 18:01:41 +02:00
/* Buffer for (part of) the parameter string descriptor */
2019-02-10 12:13:56 +02:00
__u8 * buf = NULL ;
2022-05-08 18:01:41 +02:00
/* Parameter string descriptor required length */
const int params_len_min = 18 ;
/* Parameter string descriptor accepted length */
const int params_len_max = 32 ;
/* Parameter string descriptor received length */
int params_len ;
size_t i ;
2019-02-10 12:13:56 +02:00
s32 resolution ;
/* Pen report descriptor template parameters */
2022-06-11 13:39:11 +02:00
s32 desc_params [ UCLOGIC_RDESC_PH_ID_NUM ] ;
2019-02-10 12:13:56 +02:00
__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 .
*/
2022-05-08 18:01:41 +02:00
rc = uclogic_params_get_str_desc ( & buf , hdev , 200 , params_len_max ) ;
2019-02-10 12:13:56 +02:00
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 ;
2022-05-08 18:01:41 +02:00
} else if ( rc < params_len_min ) {
2019-02-10 12:13:56 +02:00
hid_dbg ( hdev ,
2022-05-08 18:01:41 +02:00
" string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible \n " ,
rc , params_len_min ) ;
goto finish ;
}
params_len = rc ;
/*
* 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 < params_len & &
( buf [ i ] > = 0x20 & & buf [ i ] < 0x7f & & buf [ i + 1 ] = = 0 ) ;
i + = 2 ) ;
if ( i > = params_len ) {
hid_dbg ( hdev ,
" string descriptor with pen parameters seems to contain only text, assuming not compatible \n " ) ;
2019-02-10 12:13:56 +02:00
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 ;
}
/*
* 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 ;
2022-05-08 18:01:41 +02:00
if ( pparams_ptr ! = NULL ) {
* pparams_ptr = buf ;
buf = NULL ;
}
if ( pparams_len ! = NULL )
* pparams_len = params_len ;
2019-02-10 12:13:56 +02:00
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 ;
}
2022-12-26 13:54:52 +01:00
/**
* uclogic_params_cleanup_event_hooks - free resources used by the list of raw
* event hooks .
* Can be called repeatedly .
*
* @ params : Input parameters to cleanup . Cannot be NULL .
*/
static void uclogic_params_cleanup_event_hooks ( struct uclogic_params * params )
{
struct uclogic_raw_event_hook * curr , * n ;
if ( ! params | | ! params - > event_hooks )
return ;
list_for_each_entry_safe ( curr , n , & params - > event_hooks - > list , list ) {
cancel_work_sync ( & curr - > work ) ;
list_del ( & curr - > list ) ;
kfree ( curr - > event ) ;
kfree ( curr ) ;
}
kfree ( params - > event_hooks ) ;
params - > event_hooks = NULL ;
}
2019-02-10 12:13:51 +02:00
/**
* 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 ] ) ;
2022-12-26 13:54:52 +01:00
uclogic_params_cleanup_event_hooks ( params ) ;
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 ;
2022-05-08 18:01:44 +02:00
__u8 * params_ptr = NULL ;
size_t params_len = 0 ;
/* Parameters string descriptor of a model with touch ring (HS610) */
const __u8 touch_ring_model_params_buf [ ] = {
0x13 , 0x03 , 0x70 , 0xC6 , 0x00 , 0x06 , 0x7C , 0x00 ,
0xFF , 0x1F , 0xD8 , 0x13 , 0x03 , 0x0D , 0x10 , 0x01 ,
0x04 , 0x3C , 0x3E
} ;
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 */
2022-05-08 18:01:41 +02:00
rc = uclogic_params_pen_init_v2 ( & p . pen , & found ,
2022-05-08 18:01:44 +02:00
& params_ptr , & params_len ,
hdev ) ;
2019-02-10 12:13:56 +02:00
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
2022-05-08 18:01:44 +02:00
/* Link from pen sub-report */
p . pen . subreport_list [ 0 ] . value = 0xe0 ;
p . pen . subreport_list [ 0 ] . id =
UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID ;
/* If this is the model with touch ring */
if ( params_ptr ! = NULL & &
params_len = = sizeof ( touch_ring_model_params_buf ) & &
memcmp ( params_ptr , touch_ring_model_params_buf ,
params_len ) = = 0 ) {
/* Create touch ring parameters */
rc = uclogic_params_frame_init_with_desc (
2022-03-03 08:47:34 +01:00
& p . frame_list [ 1 ] ,
uclogic_rdesc_v2_frame_touch_ring_arr ,
uclogic_rdesc_v2_frame_touch_ring_size ,
2022-05-08 18:01:42 +02:00
UCLOGIC_RDESC_V2_FRAME_TOUCH_ID ) ;
2022-05-08 18:01:44 +02:00
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_DEV_ID_BYTE ;
p . frame_list [ 1 ] . touch_byte = 5 ;
p . frame_list [ 1 ] . touch_max = 12 ;
p . frame_list [ 1 ] . touch_flip_at = 7 ;
} else {
/* Create touch strip parameters */
rc = uclogic_params_frame_init_with_desc (
& p . frame_list [ 1 ] ,
uclogic_rdesc_v2_frame_touch_strip_arr ,
uclogic_rdesc_v2_frame_touch_strip_size ,
UCLOGIC_RDESC_V2_FRAME_TOUCH_ID ) ;
if ( rc ! = 0 ) {
hid_err ( hdev ,
" failed creating v2 frame touch strip parameters: %d \n " ,
rc ) ;
goto cleanup ;
}
p . frame_list [ 1 ] . suffix = " Touch Strip " ;
p . frame_list [ 1 ] . dev_id_byte =
UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE ;
p . frame_list [ 1 ] . touch_byte = 5 ;
p . frame_list [ 1 ] . touch_max = 8 ;
2022-03-03 08:47:34 +01:00
}
2022-05-08 18:01:44 +02:00
/* Link from pen sub-report */
p . pen . subreport_list [ 1 ] . value = 0xf0 ;
p . pen . subreport_list [ 1 ] . id =
UCLOGIC_RDESC_V2_FRAME_TOUCH_ID ;
2022-03-03 08:47:34 +01:00
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-05-08 18:01:44 +02:00
/* Link from pen sub-report */
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 ;
2022-05-08 18:01:44 +02:00
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 :
2022-05-08 18:01:44 +02:00
kfree ( params_ptr ) ;
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 ;
}
2022-06-11 13:39:14 +02:00
/**
* uclogic_probe_interface ( ) - some tablets , like the Parblo A610 PLUS V2 or
* the XP - PEN Deco Mini 7 , need to be initialized by sending them magic data .
*
* @ hdev : The HID device of the tablet interface to initialize and get
* parameters from . Cannot be NULL .
* @ magic_arr : The magic data that should be sent to probe the interface .
* Cannot be NULL .
* @ magic_size : Size of the magic data .
* @ endpoint : Endpoint where the magic data should be sent .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
2022-12-26 13:54:51 +01:00
static int uclogic_probe_interface ( struct hid_device * hdev , const u8 * magic_arr ,
size_t magic_size , int endpoint )
2022-06-11 13:39:14 +02:00
{
struct usb_device * udev ;
unsigned int pipe = 0 ;
int sent ;
u8 * buf = NULL ;
int rc = 0 ;
if ( ! hdev | | ! magic_arr ) {
rc = - EINVAL ;
goto cleanup ;
}
buf = kmemdup ( magic_arr , magic_size , GFP_KERNEL ) ;
if ( ! buf ) {
rc = - ENOMEM ;
goto cleanup ;
}
udev = hid_to_usb_dev ( hdev ) ;
pipe = usb_sndintpipe ( udev , endpoint ) ;
rc = usb_interrupt_msg ( udev , pipe , buf , magic_size , & sent , 1000 ) ;
if ( rc | | sent ! = magic_size ) {
hid_err ( hdev , " Interface probing failed: %d \n " , rc ) ;
rc = - 1 ;
goto cleanup ;
}
rc = 0 ;
cleanup :
kfree ( buf ) ;
return rc ;
}
2022-08-15 16:29:50 +02:00
/**
* uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing
* pen and frame parameters returned by UGEE v2 devices .
*
* @ str_desc : String descriptor , cannot be NULL .
* @ str_desc_size : Size of the string descriptor .
* @ desc_params : Output description params list .
* @ desc_params_size : Size of the output description params list .
2022-08-15 16:29:52 +02:00
* @ frame_type : Output frame type .
2022-08-15 16:29:50 +02:00
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_parse_ugee_v2_desc ( const __u8 * str_desc ,
size_t str_desc_size ,
s32 * desc_params ,
2022-08-15 16:29:52 +02:00
size_t desc_params_size ,
enum uclogic_params_frame_type * frame_type )
2022-08-15 16:29:50 +02:00
{
s32 pen_x_lm , pen_y_lm ;
s32 pen_x_pm , pen_y_pm ;
s32 pen_pressure_lm ;
s32 frame_num_buttons ;
s32 resolution ;
/* Minimum descriptor length required, maximum seen so far is 14 */
const int min_str_desc_size = 12 ;
if ( ! str_desc | | str_desc_size < min_str_desc_size )
return - EINVAL ;
if ( desc_params_size ! = UCLOGIC_RDESC_PH_ID_NUM )
return - EINVAL ;
pen_x_lm = get_unaligned_le16 ( str_desc + 2 ) ;
pen_y_lm = get_unaligned_le16 ( str_desc + 4 ) ;
frame_num_buttons = str_desc [ 6 ] ;
2022-08-15 16:29:52 +02:00
* frame_type = str_desc [ 7 ] ;
2022-08-15 16:29:50 +02:00
pen_pressure_lm = get_unaligned_le16 ( str_desc + 8 ) ;
resolution = get_unaligned_le16 ( str_desc + 10 ) ;
if ( resolution = = 0 ) {
pen_x_pm = 0 ;
pen_y_pm = 0 ;
} else {
pen_x_pm = pen_x_lm * 1000 / resolution ;
pen_y_pm = pen_y_lm * 1000 / resolution ;
}
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_LM ] = pen_x_lm ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_X_PM ] = pen_x_pm ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_LM ] = pen_y_lm ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_Y_PM ] = pen_y_pm ;
desc_params [ UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM ] = pen_pressure_lm ;
desc_params [ UCLOGIC_RDESC_FRAME_PH_ID_UM ] = frame_num_buttons ;
return 0 ;
}
2022-08-15 16:29:51 +02:00
/**
* uclogic_params_ugee_v2_init_frame_buttons ( ) - initialize a UGEE v2 frame with
* buttons .
* @ p : Parameters to fill in , cannot be NULL .
* @ desc_params : Device description params list .
* @ desc_params_size : Size of the description params list .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_ugee_v2_init_frame_buttons ( struct uclogic_params * p ,
const s32 * desc_params ,
size_t desc_params_size )
{
__u8 * rdesc_frame = NULL ;
int rc = 0 ;
if ( ! p | | desc_params_size ! = UCLOGIC_RDESC_PH_ID_NUM )
return - EINVAL ;
rdesc_frame = uclogic_rdesc_template_apply (
uclogic_rdesc_ugee_v2_frame_btn_template_arr ,
uclogic_rdesc_ugee_v2_frame_btn_template_size ,
desc_params , UCLOGIC_RDESC_PH_ID_NUM ) ;
if ( ! rdesc_frame )
return - ENOMEM ;
rc = uclogic_params_frame_init_with_desc ( & p - > frame_list [ 0 ] ,
rdesc_frame ,
uclogic_rdesc_ugee_v2_frame_btn_template_size ,
UCLOGIC_RDESC_V1_FRAME_ID ) ;
kfree ( rdesc_frame ) ;
return rc ;
}
2022-08-15 16:29:53 +02:00
/**
* uclogic_params_ugee_v2_init_frame_dial ( ) - initialize a UGEE v2 frame with a
* bitmap dial .
* @ p : Parameters to fill in , cannot be NULL .
* @ desc_params : Device description params list .
* @ desc_params_size : Size of the description params list .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_ugee_v2_init_frame_dial ( struct uclogic_params * p ,
const s32 * desc_params ,
size_t desc_params_size )
{
__u8 * rdesc_frame = NULL ;
int rc = 0 ;
if ( ! p | | desc_params_size ! = UCLOGIC_RDESC_PH_ID_NUM )
return - EINVAL ;
rdesc_frame = uclogic_rdesc_template_apply (
uclogic_rdesc_ugee_v2_frame_dial_template_arr ,
uclogic_rdesc_ugee_v2_frame_dial_template_size ,
desc_params , UCLOGIC_RDESC_PH_ID_NUM ) ;
if ( ! rdesc_frame )
return - ENOMEM ;
rc = uclogic_params_frame_init_with_desc ( & p - > frame_list [ 0 ] ,
rdesc_frame ,
uclogic_rdesc_ugee_v2_frame_dial_template_size ,
UCLOGIC_RDESC_V1_FRAME_ID ) ;
kfree ( rdesc_frame ) ;
if ( rc )
return rc ;
p - > frame_list [ 0 ] . bitmap_dial_byte = 7 ;
return 0 ;
}
2022-08-15 16:29:54 +02:00
/**
* uclogic_params_ugee_v2_init_frame_mouse ( ) - initialize a UGEE v2 frame with a
* mouse .
* @ p : Parameters to fill in , cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_ugee_v2_init_frame_mouse ( struct uclogic_params * p )
{
int rc = 0 ;
if ( ! p )
return - EINVAL ;
rc = uclogic_params_frame_init_with_desc ( & p - > frame_list [ 1 ] ,
uclogic_rdesc_ugee_v2_frame_mouse_template_arr ,
uclogic_rdesc_ugee_v2_frame_mouse_template_size ,
UCLOGIC_RDESC_V1_FRAME_ID ) ;
return rc ;
}
2022-10-29 18:12:40 +02:00
/**
* uclogic_params_ugee_v2_has_battery ( ) - check whether a UGEE v2 device has
* battery or not .
* @ hdev : The HID device of the tablet interface .
*
* Returns :
* True if the device has battery , false otherwise .
*/
static bool uclogic_params_ugee_v2_has_battery ( struct hid_device * hdev )
{
2022-12-26 13:54:50 +01:00
struct uclogic_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
if ( drvdata - > quirks & UCLOGIC_BATTERY_QUIRK )
return true ;
2022-10-29 18:12:40 +02:00
/* The XP-PEN Deco LW vendor, product and version are identical to the
* Deco L . The only difference reported by their firmware is the product
* name . Add a quirk to support battery reporting on the wireless
* version .
*/
if ( hdev - > vendor = = USB_VENDOR_ID_UGEE & &
hdev - > product = = USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L ) {
struct usb_device * udev = hid_to_usb_dev ( hdev ) ;
if ( strstarts ( udev - > product , " Deco LW " ) )
return true ;
}
return false ;
}
/**
* uclogic_params_ugee_v2_init_battery ( ) - initialize UGEE v2 battery reporting .
* @ hdev : The HID device of the tablet interface , cannot be NULL .
* @ p : Parameters to fill in , cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_ugee_v2_init_battery ( struct hid_device * hdev ,
struct uclogic_params * p )
{
int rc = 0 ;
if ( ! hdev | | ! p )
return - EINVAL ;
/* Some tablets contain invalid characters in hdev->uniq, throwing a
* " hwmon: '<name>' is not a valid name attribute, please fix " error .
* Use the device vendor and product IDs instead .
*/
snprintf ( hdev - > uniq , sizeof ( hdev - > uniq ) , " %x-%x " , hdev - > vendor ,
hdev - > product ) ;
rc = uclogic_params_frame_init_with_desc ( & p - > frame_list [ 1 ] ,
uclogic_rdesc_ugee_v2_battery_template_arr ,
uclogic_rdesc_ugee_v2_battery_template_size ,
UCLOGIC_RDESC_UGEE_V2_BATTERY_ID ) ;
if ( rc )
return rc ;
p - > frame_list [ 1 ] . suffix = " Battery " ;
p - > pen . subreport_list [ 1 ] . value = 0xf2 ;
p - > pen . subreport_list [ 1 ] . id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID ;
return rc ;
}
2022-12-26 13:54:52 +01:00
/**
* uclogic_params_ugee_v2_reconnect_work ( ) - When a wireless tablet looses
* connection to the USB dongle and reconnects , either because of its physical
* distance or because it was switches off and on using the frame ' s switch ,
* uclogic_probe_interface ( ) needs to be called again to enable the tablet .
*
* @ work : The work that triggered this function .
*/
static void uclogic_params_ugee_v2_reconnect_work ( struct work_struct * work )
{
struct uclogic_raw_event_hook * event_hook ;
event_hook = container_of ( work , struct uclogic_raw_event_hook , work ) ;
uclogic_probe_interface ( event_hook - > hdev , uclogic_ugee_v2_probe_arr ,
uclogic_ugee_v2_probe_size ,
uclogic_ugee_v2_probe_endpoint ) ;
}
/**
* uclogic_params_ugee_v2_init_event_hooks ( ) - initialize the list of events
* to be hooked for UGEE v2 devices .
* @ hdev : The HID device of the tablet interface to initialize and get
* parameters from .
* @ p : Parameters to fill in , cannot be NULL .
*
* Returns :
* Zero , if successful . A negative errno code on error .
*/
static int uclogic_params_ugee_v2_init_event_hooks ( struct hid_device * hdev ,
struct uclogic_params * p )
{
struct uclogic_raw_event_hook * event_hook ;
__u8 reconnect_event [ ] = {
/* Event received on wireless tablet reconnection */
0x02 , 0xF8 , 0x02 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
if ( ! p )
return - EINVAL ;
/* The reconnection event is only received if the tablet has battery */
if ( ! uclogic_params_ugee_v2_has_battery ( hdev ) )
return 0 ;
p - > event_hooks = kzalloc ( sizeof ( * p - > event_hooks ) , GFP_KERNEL ) ;
if ( ! p - > event_hooks )
return - ENOMEM ;
INIT_LIST_HEAD ( & p - > event_hooks - > list ) ;
event_hook = kzalloc ( sizeof ( * event_hook ) , GFP_KERNEL ) ;
if ( ! event_hook )
return - ENOMEM ;
INIT_WORK ( & event_hook - > work , uclogic_params_ugee_v2_reconnect_work ) ;
event_hook - > hdev = hdev ;
event_hook - > size = ARRAY_SIZE ( reconnect_event ) ;
event_hook - > event = kmemdup ( reconnect_event , event_hook - > size , GFP_KERNEL ) ;
if ( ! event_hook - > event )
return - ENOMEM ;
list_add_tail ( & event_hook - > list , & p - > event_hooks - > list ) ;
return 0 ;
}
2022-06-11 13:39:14 +02:00
/**
* uclogic_params_ugee_v2_init ( ) - initialize a UGEE graphics tablets by
* discovering their parameters .
*
* These tables , internally designed as v2 to differentiate them from older
* models , expect a payload of magic data in orther to be switched to the fully
* functional mode and expose their parameters in a similar way to the
* information present in uclogic_params_pen_init_v1 ( ) but with some
* differences .
*
* @ 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_ugee_v2_init ( struct uclogic_params * params ,
struct hid_device * hdev )
{
int rc = 0 ;
2022-12-26 13:54:49 +01:00
struct uclogic_drvdata * drvdata ;
2022-06-11 13:39:14 +02:00
struct usb_interface * iface ;
__u8 bInterfaceNumber ;
const int str_desc_len = 12 ;
__u8 * str_desc = NULL ;
__u8 * rdesc_pen = NULL ;
s32 desc_params [ UCLOGIC_RDESC_PH_ID_NUM ] ;
2022-08-15 16:29:52 +02:00
enum uclogic_params_frame_type frame_type ;
2022-06-11 13:39:14 +02:00
/* The resulting parameters (noop) */
struct uclogic_params p = { 0 , } ;
if ( ! params | | ! hdev ) {
rc = - EINVAL ;
goto cleanup ;
}
2022-12-26 13:54:49 +01:00
drvdata = hid_get_drvdata ( hdev ) ;
2022-06-11 13:39:14 +02:00
iface = to_usb_interface ( hdev - > dev . parent ) ;
bInterfaceNumber = iface - > cur_altsetting - > desc . bInterfaceNumber ;
2022-08-15 16:29:54 +02:00
if ( bInterfaceNumber = = 0 ) {
rc = uclogic_params_ugee_v2_init_frame_mouse ( & p ) ;
if ( rc )
goto cleanup ;
goto output ;
}
2022-06-11 13:39:14 +02:00
if ( bInterfaceNumber ! = 2 ) {
uclogic_params_init_invalid ( & p ) ;
goto output ;
}
/*
* Initialize the interface by sending magic data .
* The specific data was discovered by sniffing the Windows driver
* traffic .
*/
2022-12-26 13:54:51 +01:00
rc = uclogic_probe_interface ( hdev , uclogic_ugee_v2_probe_arr ,
uclogic_ugee_v2_probe_size ,
uclogic_ugee_v2_probe_endpoint ) ;
2022-06-11 13:39:14 +02:00
if ( rc ) {
uclogic_params_init_invalid ( & p ) ;
goto output ;
}
/*
* Read the string descriptor containing pen and frame parameters .
* The specific string descriptor and data were discovered by sniffing
* the Windows driver traffic .
*/
rc = uclogic_params_get_str_desc ( & str_desc , hdev , 100 , str_desc_len ) ;
if ( rc ! = str_desc_len ) {
hid_err ( hdev , " failed retrieving pen and frame parameters: %d \n " , rc ) ;
uclogic_params_init_invalid ( & p ) ;
goto output ;
}
2022-08-15 16:29:50 +02:00
rc = uclogic_params_parse_ugee_v2_desc ( str_desc , str_desc_len ,
desc_params ,
2022-08-15 16:29:52 +02:00
ARRAY_SIZE ( desc_params ) ,
& frame_type ) ;
2022-08-15 16:29:50 +02:00
if ( rc )
goto cleanup ;
2022-06-11 13:39:14 +02:00
kfree ( str_desc ) ;
str_desc = NULL ;
/* Initialize the pen interface */
rdesc_pen = uclogic_rdesc_template_apply (
uclogic_rdesc_ugee_v2_pen_template_arr ,
uclogic_rdesc_ugee_v2_pen_template_size ,
desc_params , ARRAY_SIZE ( desc_params ) ) ;
if ( ! rdesc_pen ) {
rc = - ENOMEM ;
goto cleanup ;
}
p . pen . desc_ptr = rdesc_pen ;
p . pen . desc_size = uclogic_rdesc_ugee_v2_pen_template_size ;
p . pen . id = 0x02 ;
p . pen . subreport_list [ 0 ] . value = 0xf0 ;
p . pen . subreport_list [ 0 ] . id = UCLOGIC_RDESC_V1_FRAME_ID ;
/* Initialize the frame interface */
2022-12-26 13:54:49 +01:00
if ( drvdata - > quirks & UCLOGIC_MOUSE_FRAME_QUIRK )
frame_type = UCLOGIC_PARAMS_FRAME_MOUSE ;
2022-08-15 16:29:52 +02:00
switch ( frame_type ) {
2022-08-15 16:29:53 +02:00
case UCLOGIC_PARAMS_FRAME_DIAL :
case UCLOGIC_PARAMS_FRAME_MOUSE :
rc = uclogic_params_ugee_v2_init_frame_dial ( & p , desc_params ,
ARRAY_SIZE ( desc_params ) ) ;
break ;
2022-08-15 16:29:52 +02:00
case UCLOGIC_PARAMS_FRAME_BUTTONS :
default :
rc = uclogic_params_ugee_v2_init_frame_buttons ( & p , desc_params ,
ARRAY_SIZE ( desc_params ) ) ;
break ;
}
2022-08-15 16:29:51 +02:00
if ( rc )
2022-06-11 13:39:14 +02:00
goto cleanup ;
2022-10-29 18:12:40 +02:00
/* Initialize the battery interface*/
if ( uclogic_params_ugee_v2_has_battery ( hdev ) ) {
rc = uclogic_params_ugee_v2_init_battery ( hdev , & p ) ;
if ( rc ) {
hid_err ( hdev , " error initializing battery: %d \n " , rc ) ;
goto cleanup ;
}
}
2022-12-26 13:54:52 +01:00
/* Create a list of raw events to be ignored */
rc = uclogic_params_ugee_v2_init_event_hooks ( hdev , & p ) ;
if ( rc ) {
hid_err ( hdev , " error initializing event hook list: %d \n " , rc ) ;
goto cleanup ;
}
2022-06-11 13:39:14 +02:00
output :
/* Output parameters */
memcpy ( params , & p , sizeof ( * params ) ) ;
memset ( & p , 0 , sizeof ( p ) ) ;
rc = 0 ;
cleanup :
kfree ( str_desc ) ;
uclogic_params_cleanup ( & p ) ;
return rc ;
}
2019-02-10 12:13:51 +02:00
/**
* 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 ) :
2022-05-08 18:01:45 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 ) :
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 ;
2022-08-15 16:29:56 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_PARBLO_A610_PRO ) :
2022-12-26 13:34:56 +01:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2 ) :
2022-06-11 13:39:14 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L ) :
2022-12-26 13:54:54 +01:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW ) :
2022-08-15 16:29:55 +02:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S ) :
2022-12-26 13:54:53 +01:00
case VID_PID ( USB_VENDOR_ID_UGEE ,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW ) :
2022-06-11 13:39:14 +02:00
rc = uclogic_params_ugee_v2_init ( & p , hdev ) ;
if ( rc ! = 0 )
goto cleanup ;
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 ;
}
2022-08-15 16:29:50 +02:00
# ifdef CONFIG_HID_KUNIT_TEST
# include "hid-uclogic-params-test.c"
# endif