Search code examples
androidchidgamepad

How to implement Left and Right Analog Triggers in a HID Gamepad for Android


I am working on a HID gamepad with 4 axis(14 Bit), 16 Buttons, 2 Analog Triggers(1 Byte) and a Hat Switch. I am currently using Axis X and Y for the left analog stick, axis Rx and Ry for right analog stick and axis Z and Rz for left and right triggers. I am able to register all buttons and analog readings(which have been tested with Gamepad tester in android and jstest-gtk in linux), but the problem is that in (Android)games the triggers are supposed to be axis 7 and axis 8 and in my case the Z and Rz are assigned axes 14 and 15. I have also tried using Acceleration(axis:11), Brake(axis:12), Rudder(axis:9) and Throttle(axis:10) but the axes 7 and 8(which are the default trigger axes) are not getting assigned.

Here is my HID Report Descriptor:-

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x05,                    // USAGE (Game Pad)
0xa1, 0x01,                    // COLLECTION (Application)

0x85, 0x01,                    //   REPORT_ID (1)
0x09, 0x01,                    //   USAGE (Pointer)
0xa1, 0x00,                    //   COLLECTION (Physical)
0x09, 0x30,                    //     USAGE (X) - Left Analog Left(-ve),Right(+ve)
0x09, 0x31,                    //     USAGE (Y) - Left Analog Up(-ve), Down(+ve)
0x09, 0x33,                    //     USAGE (Rx) - Right Analog Left(-ve), Right(+ve)
0x09, 0x34,                    //     USAGE (Ry) - Right Analog Up(-ve), Down(+ve)
0x16, 0x01, 0xE0,              //     LOGICAL_MINIMUM (-8191)
0x26, 0xFF, 0x1F,              //     LOGICAL_MAXIMUM (8191)        
0x75, 0x10,                    //     REPORT_SIZE (16)
0x95, 0x04,                    //     REPORT_COUNT (4)
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0xc0,                          //   END_COLLECTION
0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop) 
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00,              //     LOGICAL_MAXIMUM (255)
0x09, 0x32,                    //     USAGE (Z) - L2 Trigger(Shoulderpads Trigger)
0x09, 0x35,                    //     USAGE (Rz) - R2 Trigger(Shoulderpads Trigger)    
0x75, 0x08,                    //     REPORT_SIZE (8)    
0x95, 0x02,                    //     REPORT_COUNT (2)    
0x81, 0x02,                    //     INPUT (Data,Var,Abs)    

0x05, 0x09,                    //   USAGE_PAGE (Button)
0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)    
0x29, 0x10,                    //   USAGE_MAXIMUM (Button 16)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)     
0x95, 0x10,                    //   REPORT_COUNT (16)
0x75, 0x01,                    //   REPORT_SIZE (1)
0x81, 0x02,                    //   INPUT (Data,Var,Abs)

0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
0x09, 0x39,                    //   USAGE (Hat switch)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x46, 0x3b, 0x01,              //   PHYSICAL_MAXIMUM (315)
0x65, 0x14,                    //   UNIT (Eng Rotation:Centimeter)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)

0xc0                           // END_COLLECTION

Any Help will be deeply appreciated, Thank You.

Edit:- Here is the new report descriptor I tried based on the Anrdoid CDD document specified by Nipo:-

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x05,                    // USAGE (Game Pad)
0xa1, 0x01,                    // COLLECTION (Application)
0x85, 0x01,                    //   REPORT_ID (1)
0x09, 0x01,                    //   USAGE (Pointer)
0xa1, 0x00,                    //   COLLECTION (Physical)
0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
0x09, 0x30,                    //     USAGE (X) - Left Analog Left(-ve),Right(+ve)
0x09, 0x31,                    //     USAGE (Y) - Left Analog Up(-ve), Down(+ve)
0x09, 0x32,                    //     USAGE (Z) - Right Analog X
0x09, 0x35,                    //     USAGE (Rz) - Right Analog Y
0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
0x25, 0x7F,                    //     LOGICAL_MAXIMUM (127)       
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x04,                    //     REPORT_COUNT (4)
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0x05, 0x02,                    // USAGE_PAGE (Simulation Control)            
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00,              //     LOGICAL_MAXIMUM (255)        
0x09, 0xC4,                    //     USAGE(Acceleration)
0x09, 0xC5,                    //     USAGE(Brake)        
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x02,                    //     REPORT_COUNT (2)    
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0x05, 0x09,                    //   USAGE_PAGE (Button)
0x09, 0x01,                    //   USAGE(Button 1)
0x09, 0x02,                    //   USAGE(Button 2)
0x09, 0x04,                    //   USAGE(Button 4)
0x09, 0x05,                    //   USAGE(Button 5)
0x09, 0x07,                    //   USAGE(Button 7)
0x09, 0x08,                    //   USAGE(Button 8)
0x09, 0x0E,                    //   USAGE(Button 14)
0x09, 0x0F,                    //   USAGE(Button 15)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)     
0x95, 0x08,                    //   REPORT_COUNT (8)
0x75, 0x01,                    //   REPORT_SIZE (1)
0x81, 0x02,                    //   INPUT (Data,Var,Abs)           
0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
0x09, 0x39,                    //   USAGE (Hat switch)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x46, 0x3b, 0x01,              //   PHYSICAL_MAXIMUM (315)
0x65, 0x14,                    //   UNIT (Eng Rotation:Centimeter)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x65, 0x00,                    // Unit (None)
/*!@ The below section is taken from the page 31 of the document given
  in the link below.
  https://www.silabs.com/documents/public/application-notes/AN993.pdf
 */
0x05, 0x0C,                    //   USAGE_PAGE (Consumer)
0x0A, 0x23, 0x02,              //   USAGE (AC Home)
0x0A, 0x24, 0x02,              //   USAGE (AC Back)
0x75, 0x01,                    //   REPORT_SIZE(1)
0x95, 0x02,                    //   REPORT_COUNT(2)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x45, 0x01,                    //   PHYSICAL_MAXIMUM (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x95, 0x01,                    //   REPORT_COUNT (2)
0x75, 0x02,                    //   REPORT_SIZE (1)
0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)
0xc0,                          //   END_COLLECTION
0xc0,                          //   END_COLLECTION

Solution

  • Here is the Report Descriptor that worked for me:-

    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x05,                    // USAGE (Game Pad)
    0xa1, 0x01,                    // COLLECTION (Application)    
    
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)    
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X) - Left Analog Left(-ve),Right(+ve)
    0x09, 0x31,                    //     USAGE (Y) - Left Analog Up(-ve), Down(+ve)    
    0x09, 0x32,                    //     USAGE (Z) - Right Analog Left(-ve)Right(+ve)
    0x09, 0x35,                    //     USAGE (Rz)- Right Analog Up(-ve), Down(+ve)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    
    0x05, 0x02,                    //     USAGE_PAGE (Simulation Control)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x26, 0xFF, 0x00,              //     LOGICAL_MAXIMUM (255)
    0x09, 0xC4,                    //     USAGE(Acceleration)
    0x09, 0xC5,                    //     USAGE(Brake)           
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    
    0x05, 0x09,                    //   USAGE_PAGE (Button)
    0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
    0x29, 0x10,                    //   USAGE_MAXIMUM (Button 16)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x95, 0x10,                    //   REPORT_COUNT (16)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    
    0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
    0x09, 0x39,                    //   USAGE (Hat switch)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
    0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
    0x46, 0x3b, 0x01,              //   PHYSICAL_MAXIMUM (315)
    0x65, 0x14,                    //   UNIT (Eng Rotation:Centimeter)
    0x75, 0x04,                    //   REPORT_SIZE (4)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x04,                    //   REPORT_SIZE (4)
    0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)
    0x65, 0x00,                    //   Unit (None)
    
    0x05, 0x0C,                    //   USAGE_PAGE (Consumer)
    0x0A, 0x23, 0x02,              //   USAGE (AC Home)
    0x0A, 0x24, 0x02,              //   USAGE (AC Back)
    0x75, 0x01,                    //   REPORT_SIZE(1)
    0x95, 0x02,                    //   REPORT_COUNT(2)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
    0x45, 0x01,                    //   PHYSICAL_MAXIMUM (1)
    0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
    
    0x95, 0x01,                    //   REPORT_COUNT (2)
    0x75, 0x02,                    //   REPORT_SIZE (1)
    0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)
    
    0xc0                           // END_COLLECTION
    

    Here are some findings that I was able to see while testing:-

    1. But the above descriptor will not work in every game, what I was able to find is that some games in Android has the option to configure/customize the button mapping like the DEAD TRIGGER 2 - Zombie Survival Shooter FPS, but in such games the default axis for the right joystick is the Rx and Ry, so in such games the right joystick and the triggers(the axes used for triggers is still a mystery) must be mapped explicitly.
    2. But in games where there is no option to change the buttons mapping this HID descriptor will work correctly even the triggers(this made me believe that this is the correct way to define the descriptor).