From: Aric Stewart Subject: [PATCH v4 3/7] winebus.sys: Build SDL device report Message-Id: <79f5f736-d212-dc8c-48db-23c67923f4f8@codeweavers.com> Date: Wed, 21 Feb 2018 06:56:31 -0600 v2: Correct ifdef value v3: dynamic loading of the SDL library v4: Dynamically use Joystick Vendor and Product ID if available Signed-off-by: Aric Stewart --- dlls/winebus.sys/Makefile.in | 1 + dlls/winebus.sys/bus_sdl.c | 197 +++++++++++++++++++++++++++++++++++++++++- dlls/winebus.sys/bus_udev.c | 95 +------------------- dlls/winebus.sys/controller.c | 48 ++++++++++ dlls/winebus.sys/controller.h | 96 ++++++++++++++++++++ 5 files changed, 342 insertions(+), 95 deletions(-) create mode 100644 dlls/winebus.sys/controller.c create mode 100644 dlls/winebus.sys/controller.h diff --git a/dlls/winebus.sys/Makefile.in b/dlls/winebus.sys/Makefile.in index 2d7c6eb0c4..a01758ebc5 100644 --- a/dlls/winebus.sys/Makefile.in +++ b/dlls/winebus.sys/Makefile.in @@ -8,4 +8,5 @@ C_SRCS = \ bus_iohid.c \ bus_sdl.c \ bus_udev.c \ + controller.c \ main.c diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 1a02888f92..5f422a501e 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -45,6 +45,7 @@ #include "wine/debug.h" #include "wine/unicode.h" #include "hidusage.h" +#include "controller.h" #ifdef WORDS_BIGENDIAN # define LE_WORD(x) RtlUshortByteSwap(x) @@ -81,6 +82,9 @@ MAKE_FUNCPTR(SDL_JoystickName); MAKE_FUNCPTR(SDL_JoystickNumAxes); MAKE_FUNCPTR(SDL_JoystickOpen); MAKE_FUNCPTR(SDL_WaitEvent); +MAKE_FUNCPTR(SDL_JoystickNumButtons); +MAKE_FUNCPTR(SDL_JoystickNumBalls); +MAKE_FUNCPTR(SDL_JoystickNumHats); #endif static Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick); static Uint16 (*pSDL_JoystickGetProductVersion)(SDL_Joystick * joystick); @@ -90,6 +94,13 @@ struct platform_private { SDL_Joystick *sdl_joystick; SDL_JoystickID id; + + int axis_start; + int ball_start; + int hat_start; + + int report_descriptor_size; + BYTE *report_descriptor; }; static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device) @@ -97,6 +108,171 @@ static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *de return (struct platform_private *)get_platform_private(device); } +static const BYTE REPORT_AXIS_TAIL[] = { + 0x16, 0x00, 0x80, /* LOGICAL_MINIMUM (-32768) */ + 0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (32767) */ + 0x36, 0x00, 0x80, /* PHYSICAL_MINIMUM (-32768) */ + 0x46, 0xff, 0x7f, /* PHYSICAL_MAXIMUM (32767) */ + 0x75, 0x10, /* REPORT_SIZE (16) */ + 0x95, 0x00, /* REPORT_COUNT (?) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ +}; +#define IDX_ABS_AXIS_COUNT 15 + +static BYTE *add_axis_block(BYTE *report_ptr, BYTE count, BYTE page, const BYTE *usages, BOOL absolute) +{ + int i; + memcpy(report_ptr, REPORT_AXIS_HEADER, sizeof(REPORT_AXIS_HEADER)); + report_ptr[IDX_AXIS_PAGE] = page; + report_ptr += sizeof(REPORT_AXIS_HEADER); + for (i = 0; i < count; i++) + { + memcpy(report_ptr, REPORT_AXIS_USAGE, sizeof(REPORT_AXIS_USAGE)); + report_ptr[IDX_AXIS_USAGE] = usages[i]; + report_ptr += sizeof(REPORT_AXIS_USAGE); + } + if (absolute) + { + memcpy(report_ptr, REPORT_AXIS_TAIL, sizeof(REPORT_AXIS_TAIL)); + report_ptr[IDX_ABS_AXIS_COUNT] = count; + report_ptr += sizeof(REPORT_AXIS_TAIL); + } + else + { + memcpy(report_ptr, REPORT_REL_AXIS_TAIL, sizeof(REPORT_REL_AXIS_TAIL)); + report_ptr[IDX_REL_AXIS_COUNT] = count; + report_ptr += sizeof(REPORT_REL_AXIS_TAIL); + } + return report_ptr; +} + +static BOOL build_report_descriptor(struct platform_private *ext) +{ + BYTE *report_ptr; + INT i, descript_size; + INT report_size; + INT button_count, axis_count, ball_count, hat_count; + static const BYTE device_usage[2] = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_GAMEPAD}; + static const BYTE controller_usages[] = { + HID_USAGE_GENERIC_X, + HID_USAGE_GENERIC_Y, + HID_USAGE_GENERIC_Z, + HID_USAGE_GENERIC_RX, + HID_USAGE_GENERIC_RY, + HID_USAGE_GENERIC_RZ, + HID_USAGE_GENERIC_SLIDER, + HID_USAGE_GENERIC_DIAL, + HID_USAGE_GENERIC_WHEEL}; + static const BYTE joystick_usages[] = { + HID_USAGE_GENERIC_X, + HID_USAGE_GENERIC_Y, + HID_USAGE_GENERIC_Z, + HID_USAGE_GENERIC_RZ, + HID_USAGE_GENERIC_RX, + HID_USAGE_GENERIC_RY, + HID_USAGE_GENERIC_SLIDER, + HID_USAGE_GENERIC_DIAL, + HID_USAGE_GENERIC_WHEEL}; + + descript_size = sizeof(REPORT_HEADER) + sizeof(REPORT_TAIL); + report_size = 0; + + /* For now lump all buttons just into incremental usages, Ignore Keys */ + button_count = pSDL_JoystickNumButtons(ext->sdl_joystick); + if (button_count) + { + descript_size += sizeof(REPORT_BUTTONS); + if (button_count % 8) + descript_size += sizeof(REPORT_PADDING); + report_size = (button_count + 7) / 8; + } + + axis_count = pSDL_JoystickNumAxes(ext->sdl_joystick); + if (axis_count > 6) + { + FIXME("Clamping joystick to 6 axis\n"); + axis_count = 6; + } + + ext->axis_start = report_size; + if (axis_count) + { + descript_size += sizeof(REPORT_AXIS_HEADER); + descript_size += (sizeof(REPORT_AXIS_USAGE) * axis_count); + descript_size += sizeof(REPORT_AXIS_TAIL); + report_size += (2 * axis_count); + } + + ball_count = pSDL_JoystickNumBalls(ext->sdl_joystick); + ext->ball_start = report_size; + if (ball_count) + { + if ((ball_count*2) + axis_count > 9) + { + FIXME("Capping ball + axis at 9\n"); + ball_count = (9-axis_count)/2; + } + descript_size += sizeof(REPORT_AXIS_HEADER); + descript_size += (sizeof(REPORT_AXIS_USAGE) * ball_count * 2); + descript_size += sizeof(REPORT_REL_AXIS_TAIL); + report_size += (2*ball_count); + } + + hat_count = pSDL_JoystickNumHats(ext->sdl_joystick); + ext->hat_start = report_size; + if (hat_count) + { + descript_size += sizeof(REPORT_HATSWITCH); + for (i = 0; i < hat_count; i++) + report_size++; + } + + TRACE("Report Descriptor will be %i bytes\n", descript_size); + TRACE("Report will be %i bytes\n", report_size); + + ext->report_descriptor = HeapAlloc(GetProcessHeap(), 0, descript_size); + if (!ext->report_descriptor) + { + ERR("Failed to alloc report descriptor\n"); + return FALSE; + } + report_ptr = ext->report_descriptor; + + memcpy(report_ptr, REPORT_HEADER, sizeof(REPORT_HEADER)); + report_ptr[IDX_HEADER_PAGE] = device_usage[0]; + report_ptr[IDX_HEADER_USAGE] = device_usage[1]; + report_ptr += sizeof(REPORT_HEADER); + if (button_count) + { + report_ptr = add_button_block(report_ptr, 1, button_count); + if (button_count % 8) + { + BYTE padding = 8 - (button_count % 8); + report_ptr = add_padding_block(report_ptr, padding); + } + } + if (axis_count) + { + if (axis_count == 6 && button_count >= 14) + report_ptr = add_axis_block(report_ptr, axis_count, HID_USAGE_PAGE_GENERIC, controller_usages, TRUE); + else + report_ptr = add_axis_block(report_ptr, axis_count, HID_USAGE_PAGE_GENERIC, joystick_usages, TRUE); + + } + if (ball_count) + { + report_ptr = add_axis_block(report_ptr, ball_count * 2, HID_USAGE_PAGE_GENERIC, &joystick_usages[axis_count], FALSE); + } + if (hat_count) + report_ptr = add_hatswitch(report_ptr, hat_count); + + memcpy(report_ptr, REPORT_TAIL, sizeof(REPORT_TAIL)); + + ext->report_descriptor_size = descript_size; + + return TRUE; +} + static int compare_platform_device(DEVICE_OBJECT *device, void *platform_dev) { SDL_JoystickID id1 = impl_from_DEVICE_OBJECT(device)->id; @@ -106,7 +282,16 @@ static int compare_platform_device(DEVICE_OBJECT *device, void *platform_dev) static NTSTATUS get_reportdescriptor(DEVICE_OBJECT *device, BYTE *buffer, DWORD length, DWORD *out_length) { - return STATUS_NOT_IMPLEMENTED; + struct platform_private *ext = impl_from_DEVICE_OBJECT(device); + + *out_length = ext->report_descriptor_size; + + if (length < ext->report_descriptor_size) + return STATUS_BUFFER_TOO_SMALL; + + memcpy(buffer, ext->report_descriptor, ext->report_descriptor_size); + + return STATUS_SUCCESS; } static NTSTATUS get_string(DEVICE_OBJECT *device, DWORD index, WCHAR *buffer, DWORD length) @@ -233,6 +418,13 @@ static void try_add_device(SDL_JoystickID index) struct platform_private *private = impl_from_DEVICE_OBJECT(device); private->sdl_joystick = joystick; private->id = id; + if (!build_report_descriptor(private)) + { + ERR("Building report descriptor failed, removing device\n"); + bus_remove_hid_device(device); + HeapFree(GetProcessHeap(), 0, serial); + return; + } IoInvalidateDeviceRelations(device, BusRelations); } else @@ -299,6 +491,9 @@ NTSTATUS WINAPI sdl_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_ LOAD_FUNCPTR(SDL_JoystickNumAxes); LOAD_FUNCPTR(SDL_JoystickOpen); LOAD_FUNCPTR(SDL_WaitEvent); + LOAD_FUNCPTR(SDL_JoystickNumButtons); + LOAD_FUNCPTR(SDL_JoystickNumBalls); + LOAD_FUNCPTR(SDL_JoystickNumHats); #undef LOAD_FUNCPTR pSDL_JoystickGetProduct = wine_dlsym(sdl_handle, "SDL_JoystickGetProduct", NULL, 0); pSDL_JoystickGetProductVersion = wine_dlsym(sdl_handle, "SDL_JoystickGetProductVersion", NULL, 0); diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 18e1854f62..9d78f1905b 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -75,6 +75,7 @@ #define LE_DWORD(x) (x) #endif +#include "controller.h" #include "bus.h" WINE_DEFAULT_DEBUG_CHANNEL(plugplay); @@ -110,50 +111,6 @@ static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *de } #ifdef HAS_PROPER_INPUT_HEADER -static const BYTE REPORT_HEADER[] = { - 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ - 0x09, 0x00, /* USAGE (??) */ - 0xa1, 0x01, /* COLLECTION (Application) */ - 0x09, 0x01, /* USAGE () */ - 0xa1, 0x00, /* COLLECTION (Physical) */ -}; - -#define IDX_HEADER_PAGE 1 -#define IDX_HEADER_USAGE 3 - -static const BYTE REPORT_BUTTONS[] = { - 0x05, 0x09, /* USAGE_PAGE (Button) */ - 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ - 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ - 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ - 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ - 0x35, 0x00, /* LOGICAL_MINIMUM (0) */ - 0x45, 0x01, /* LOGICAL_MAXIMUM (1) */ - 0x95, 0x03, /* REPORT_COUNT (3) */ - 0x75, 0x01, /* REPORT_SIZE (1) */ - 0x81, 0x02, /* INPUT (Data,Var,Abs) */ -}; -#define IDX_BUTTON_MIN_USAGE 3 -#define IDX_BUTTON_MAX_USAGE 5 -#define IDX_BUTTON_COUNT 11 - -static const BYTE REPORT_PADDING[] = { - 0x95, 0x03, /* REPORT_COUNT (3) */ - 0x75, 0x01, /* REPORT_SIZE (1) */ - 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ -}; -#define IDX_PADDING_BIT_COUNT 1 - -static const BYTE REPORT_AXIS_HEADER[] = { - 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ -}; -#define IDX_AXIS_PAGE 1 - - -static const BYTE REPORT_AXIS_USAGE[] = { - 0x09, 0x30, /* USAGE (X) */ -}; -#define IDX_AXIS_USAGE 1 static const BYTE REPORT_ABS_AXIS_TAIL[] = { 0x17, 0x00, 0x00, 0x00, 0x00, /* LOGICAL_MINIMUM (0) */ @@ -170,33 +127,6 @@ static const BYTE REPORT_ABS_AXIS_TAIL[] = { #define IDX_ABS_PHY_MAXIMUM 16 #define IDX_ABS_AXIS_COUNT 23 -static const BYTE REPORT_REL_AXIS_TAIL[] = { - 0x15, 0x81, /* LOGICAL_MINIMUM (0) */ - 0x25, 0x7f, /* LOGICAL_MAXIMUM (0xffff) */ - 0x75, 0x08, /* REPORT_SIZE (16) */ - 0x95, 0x02, /* REPORT_COUNT (2) */ - 0x81, 0x06, /* INPUT (Data,Var,Rel) */ -}; -#define IDX_REL_AXIS_COUNT 7 - -static const BYTE REPORT_HATSWITCH[] = { - 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ - 0x09, 0x39, /* USAGE (Hatswitch) */ - 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ - 0x25, 0x08, /* LOGICAL_MAXIMUM (0x08) */ - 0x35, 0x00, /* PHYSICAL_MINIMUM (0) */ - 0x45, 0x08, /* PHYSICAL_MAXIMUM (8) */ - 0x75, 0x08, /* REPORT_SIZE (8) */ - 0x95, 0x01, /* REPORT_COUNT (1) */ - 0x81, 0x02, /* INPUT (Data,Var,Abs) */ -}; -#define IDX_HATSWITCH_COUNT 15 - -static const BYTE REPORT_TAIL[] = { - 0xc0, /* END_COLLECTION */ - 0xc0 /* END_COLLECTION */ -}; - static const BYTE ABS_TO_HID_MAP[][2] = { {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X}, /*ABS_X*/ {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y}, /*ABS_Y*/ @@ -271,15 +201,6 @@ struct wine_input_private { #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7))) -static BYTE *add_button_block(BYTE* report_ptr, BYTE usage_min, BYTE usage_max) -{ - memcpy(report_ptr, REPORT_BUTTONS, sizeof(REPORT_BUTTONS)); - report_ptr[IDX_BUTTON_MIN_USAGE] = usage_min; - report_ptr[IDX_BUTTON_MAX_USAGE] = usage_max; - report_ptr[IDX_BUTTON_COUNT] = (usage_max - usage_min) + 1; - return report_ptr + sizeof(REPORT_BUTTONS); -} - static BYTE *add_axis_block(BYTE *report_ptr, BYTE count, BYTE page, BYTE *usages, BOOL absolute, const struct wine_input_absinfo *absinfo) { int i; @@ -314,20 +235,6 @@ static BYTE *add_axis_block(BYTE *report_ptr, BYTE count, BYTE page, BYTE *usage return report_ptr; } -static BYTE *add_padding_block(BYTE *report_ptr, BYTE bitcount) -{ - memcpy(report_ptr, REPORT_PADDING, sizeof(REPORT_PADDING)); - report_ptr[IDX_PADDING_BIT_COUNT] = bitcount; - return report_ptr + sizeof(REPORT_PADDING); -} - -static BYTE *add_hatswitch(BYTE *report_ptr, INT count) -{ - memcpy(report_ptr, REPORT_HATSWITCH, sizeof(REPORT_HATSWITCH)); - report_ptr[IDX_HATSWITCH_COUNT] = count; - return report_ptr + sizeof(REPORT_HATSWITCH); -} - static const BYTE* what_am_I(struct udev_device *dev) { static const BYTE Unknown[2] = {HID_USAGE_PAGE_GENERIC, 0}; diff --git a/dlls/winebus.sys/controller.c b/dlls/winebus.sys/controller.c new file mode 100644 index 0000000000..6c707a741b --- /dev/null +++ b/dlls/winebus.sys/controller.c @@ -0,0 +1,48 @@ +/* Common controller functions and structures + * + * Copyright 2018 CodeWeavers, Aric Stewart + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define NONAMELESSUNION +#include "config.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "controller.h" + +BYTE *add_button_block(BYTE* report_ptr, BYTE usage_min, BYTE usage_max) +{ + memcpy(report_ptr, REPORT_BUTTONS, sizeof(REPORT_BUTTONS)); + report_ptr[IDX_BUTTON_MIN_USAGE] = usage_min; + report_ptr[IDX_BUTTON_MAX_USAGE] = usage_max; + report_ptr[IDX_BUTTON_COUNT] = (usage_max - usage_min) + 1; + return report_ptr + sizeof(REPORT_BUTTONS); +} + + +BYTE *add_padding_block(BYTE *report_ptr, BYTE bitcount) +{ + memcpy(report_ptr, REPORT_PADDING, sizeof(REPORT_PADDING)); + report_ptr[IDX_PADDING_BIT_COUNT] = bitcount; + return report_ptr + sizeof(REPORT_PADDING); +} + +BYTE *add_hatswitch(BYTE *report_ptr, INT count) +{ + memcpy(report_ptr, REPORT_HATSWITCH, sizeof(REPORT_HATSWITCH)); + report_ptr[IDX_HATSWITCH_COUNT] = count; + return report_ptr + sizeof(REPORT_HATSWITCH); +} diff --git a/dlls/winebus.sys/controller.h b/dlls/winebus.sys/controller.h new file mode 100644 index 0000000000..2c0eed029f --- /dev/null +++ b/dlls/winebus.sys/controller.h @@ -0,0 +1,96 @@ +/* + * Copyright 2018 Aric Stewart + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Blocks of data for building HID device descriptions */ + +#if defined(HAVE_SDL) || defined(HAVE_LINUX_INPUT_H) + +static const BYTE REPORT_HEADER[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x00, /* USAGE (??) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x09, 0x01, /* USAGE () */ + 0xa1, 0x00, /* COLLECTION (Physical) */ +}; +#define IDX_HEADER_PAGE 1 +#define IDX_HEADER_USAGE 3 + +static const BYTE REPORT_BUTTONS[] = { + 0x05, 0x09, /* USAGE_PAGE (Button) */ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x35, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x45, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ +}; +#define IDX_BUTTON_MIN_USAGE 3 +#define IDX_BUTTON_MAX_USAGE 5 +#define IDX_BUTTON_COUNT 11 + +static const BYTE REPORT_PADDING[] = { + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ +}; +#define IDX_PADDING_BIT_COUNT 1 + +static const BYTE REPORT_AXIS_HEADER[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ +}; +#define IDX_AXIS_PAGE 1 + +static const BYTE REPORT_AXIS_USAGE[] = { + 0x09, 0x30, /* USAGE (X) */ +}; +#define IDX_AXIS_USAGE 1 + +static const BYTE REPORT_REL_AXIS_TAIL[] = { + 0x15, 0x81, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x7f, /* LOGICAL_MAXIMUM (0xffff) */ + 0x75, 0x08, /* REPORT_SIZE (16) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ +}; +#define IDX_REL_AXIS_COUNT 7 + +static const BYTE REPORT_HATSWITCH[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x39, /* USAGE (Hatswitch) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x08, /* LOGICAL_MAXIMUM (0x08) */ + 0x35, 0x00, /* PHYSICAL_MINIMUM (0) */ + 0x45, 0x08, /* PHYSICAL_MAXIMUM (8) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ +}; +#define IDX_HATSWITCH_COUNT 15 + +static const BYTE REPORT_TAIL[] = { + 0xc0, /* END_COLLECTION */ + 0xc0 /* END_COLLECTION */ +}; + +BYTE *add_button_block(BYTE* report_ptr, BYTE usage_min, BYTE usage_max) DECLSPEC_HIDDEN; +BYTE *add_padding_block(BYTE *report_ptr, BYTE bitcount) DECLSPEC_HIDDEN; +BYTE *add_hatswitch(BYTE *report_ptr, INT count) DECLSPEC_HIDDEN; +#endif