Skip to content

Instantly share code, notes, and snippets.

@probonopd
Last active November 12, 2023 00:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save probonopd/9646c69f876ff2b4b879aeb1c1cbc532 to your computer and use it in GitHub Desktop.
Save probonopd/9646c69f876ff2b4b879aeb1c1cbc532 to your computer and use it in GitHub Desktop.
Automatic USB keyboard language detection

Automatic USB keyboard language detection

https://www.youtube.com/watch?v=_pWRVR1Knfo around 43:50 shows that the Apple Lisa could set the system language automatically depending on which language keyboard was attaced. In 1983!

The official Raspberry Pi keyboard can automatically tell the operating system the language, something all keyboards should have done since the introduction of USB.

Kudos to the genius at the Raspberry Pi Foundation who made this happen, finally. I only wish I could plug this keyboard into any computer system and have it just work.

How is it working, actually? Does anybody know?

In a marketing text, it is stated that Raspberry Pi Trading Ltd. "worked with Holtek on custom firmware for the key matrix management. The outcome of this is the ability for the Pi to auto-detect what country the keyboard is configured for".

To be investigated

Please post the results of

sudo lsusb -v -d 0x04d9:0x0006
sudo usbhid-dump -d 0x04d9:0x0006 | grep -v : | xxd -r -p | hidrd-convert -o code

and please mention the language of your keyboard.

German keyboard

lsusb -v

me@host:~$ sudo lsusb -v -d 0x04d9:0x0006

Bus 001 Device 003: ID 04d9:0006 Holtek Semiconductor, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d9 Holtek Semiconductor, Inc.
  idProduct          0x0006 
  bcdDevice            1.10
  iManufacturer           1  
  iProduct                2 RPI Wired Keyboard 5
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           59
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      65
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      59
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0005  1x 5 bytes
        bInterval              10
Device Status:     0x0000
  (Bus Powered)

Note the bCountryCode 0 Not supported, whatever this means...

usbhid-dump

me@host:~$ sudo usbhid-dump -d 0x04d9:0x0006
001:003:001:DESCRIPTOR         1567624861.409839
 05 0C 09 01 A1 01 05 0C 75 01 95 01 15 00 25 01
 09 CD 81 06 09 B5 81 02 09 B6 81 02 09 B8 81 06
 09 E2 81 06 09 EA 81 02 09 E9 81 02 0A 23 02 81
 02 0A 92 01 81 02 95 07 81 01 C0

001:003:000:DESCRIPTOR         1567624861.419692
 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
 75 01 95 08 81 02 95 01 75 08 81 01 95 03 75 01
 05 08 19 01 29 03 91 02 95 05 75 01 91 01 95 06
 75 08 15 00 26 FF 00 05 07 19 00 2A FF 00 81 00
 C0

hidrd-convert

me@host:~$ sudo usbhid-dump -d 0x04d9:0x0006 | grep -v : | xxd -r -p | hidrd-convert -o code
0x05, 0x0C,         /*  Usage Page (Consumer),              */
0x09, 0x01,         /*  Usage (Consumer Control),           */
0xA1, 0x01,         /*  Collection (Application),           */
0x05, 0x0C,         /*      Usage Page (Consumer),          */
0x75, 0x01,         /*      Report Size (1),                */
0x95, 0x01,         /*      Report Count (1),               */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x25, 0x01,         /*      Logical Maximum (1),            */
0x09, 0xCD,         /*      Usage (Play Pause),             */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xB5,         /*      Usage (Scan Next Track),        */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xB6,         /*      Usage (Scan Previous Track),    */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xB8,         /*      Usage (Eject),                  */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xE2,         /*      Usage (Mute),                   */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xEA,         /*      Usage (Volume Dec),             */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xE9,         /*      Usage (Volume Inc),             */
0x81, 0x02,         /*      Input (Variable),               */
0x0A, 0x23, 0x02,   /*      Usage (AC Home),                */
0x81, 0x02,         /*      Input (Variable),               */
0x0A, 0x92, 0x01,   /*      Usage (AL Calculator),          */
0x81, 0x02,         /*      Input (Variable),               */
0x95, 0x07,         /*      Report Count (7),               */
0x81, 0x01,         /*      Input (Constant),               */
0xC0,               /*  End Collection,                     */
0x05, 0x01,         /*  Usage Page (Desktop),               */
0x09, 0x06,         /*  Usage (Keyboard),                   */
0xA1, 0x01,         /*  Collection (Application),           */
0x05, 0x07,         /*      Usage Page (Keyboard),          */
0x19, 0xE0,         /*      Usage Minimum (KB Leftcontrol), */
0x29, 0xE7,         /*      Usage Maximum (KB Right GUI),   */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x25, 0x01,         /*      Logical Maximum (1),            */
0x75, 0x01,         /*      Report Size (1),                */
0x95, 0x08,         /*      Report Count (8),               */
0x81, 0x02,         /*      Input (Variable),               */
0x95, 0x01,         /*      Report Count (1),               */
0x75, 0x08,         /*      Report Size (8),                */
0x81, 0x01,         /*      Input (Constant),               */
0x95, 0x03,         /*      Report Count (3),               */
0x75, 0x01,         /*      Report Size (1),                */
0x05, 0x08,         /*      Usage Page (LED),               */
0x19, 0x01,         /*      Usage Minimum (01h),            */
0x29, 0x03,         /*      Usage Maximum (03h),            */
0x91, 0x02,         /*      Output (Variable),              */
0x95, 0x05,         /*      Report Count (5),               */
0x75, 0x01,         /*      Report Size (1),                */
0x91, 0x01,         /*      Output (Constant),              */
0x95, 0x06,         /*      Report Count (6),               */
0x75, 0x08,         /*      Report Size (8),                */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
0x05, 0x07,         /*      Usage Page (Keyboard),          */
0x19, 0x00,         /*      Usage Minimum (None),           */
0x2A, 0xFF, 0x00,   /*      Usage Maximum (FFh),            */
0x81, 0x00,         /*      Input,                          */
0xC0                /*  End Collection                      */
@zyga
Copy link

zyga commented Sep 4, 2019

Thank you for sharing. I will update once my PL (really US) keyboard arrives later this week.

@probonopd
Copy link
Author

probonopd commented Sep 5, 2019

@zyga I'd bet you'll have iProduct RPI Wired Keyboard 1 or RPI Wired Keyboard 2...

@waveform80
Copy link

British Keyboard

lsusb -v

Bus 001 Device 006: ID 04d9:0006 Holtek Semiconductor, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d9 Holtek Semiconductor, Inc.
  idProduct          0x0006 
  bcdDevice            1.10
  iManufacturer           1  
  iProduct                2 RPI Wired Keyboard 1
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           59
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      65
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      59
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0005  1x 5 bytes
        bInterval              10
Device Status:     0x0000
  (Bus Powered)

usbhid-dump

001:006:001:DESCRIPTOR         1567717984.188071
 05 0C 09 01 A1 01 05 0C 75 01 95 01 15 00 25 01
 09 CD 81 06 09 B5 81 02 09 B6 81 02 09 B8 81 06
 09 E2 81 06 09 EA 81 02 09 E9 81 02 0A 23 02 81
 02 0A 92 01 81 02 95 07 81 01 C0

001:006:000:DESCRIPTOR         1567717984.197979
 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
 75 01 95 08 81 02 95 01 75 08 81 01 95 03 75 01
 05 08 19 01 29 03 91 02 95 05 75 01 91 01 95 06
 75 08 15 00 26 FF 00 05 07 19 00 2A FF 00 81 00
 C0

hidrd-convert

0x05, 0x0C,         /*  Usage Page (Consumer),              */
0x09, 0x01,         /*  Usage (Consumer Control),           */
0xA1, 0x01,         /*  Collection (Application),           */
0x05, 0x0C,         /*      Usage Page (Consumer),          */
0x75, 0x01,         /*      Report Size (1),                */
0x95, 0x01,         /*      Report Count (1),               */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x25, 0x01,         /*      Logical Maximum (1),            */
0x09, 0xCD,         /*      Usage (Play Pause),             */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xB5,         /*      Usage (Scan Next Track),        */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xB6,         /*      Usage (Scan Previous Track),    */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xB8,         /*      Usage (Eject),                  */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xE2,         /*      Usage (Mute),                   */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xEA,         /*      Usage (Volume Dec),             */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xE9,         /*      Usage (Volume Inc),             */
0x81, 0x02,         /*      Input (Variable),               */
0x0A, 0x23, 0x02,   /*      Usage (AC Home),                */
0x81, 0x02,         /*      Input (Variable),               */
0x0A, 0x92, 0x01,   /*      Usage (AL Calculator),          */
0x81, 0x02,         /*      Input (Variable),               */
0x95, 0x07,         /*      Report Count (7),               */
0x81, 0x01,         /*      Input (Constant),               */
0xC0,               /*  End Collection,                     */
0x05, 0x01,         /*  Usage Page (Desktop),               */
0x09, 0x06,         /*  Usage (Keyboard),                   */
0xA1, 0x01,         /*  Collection (Application),           */
0x05, 0x07,         /*      Usage Page (Keyboard),          */
0x19, 0xE0,         /*      Usage Minimum (KB Leftcontrol), */
0x29, 0xE7,         /*      Usage Maximum (KB Right GUI),   */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x25, 0x01,         /*      Logical Maximum (1),            */
0x75, 0x01,         /*      Report Size (1),                */
0x95, 0x08,         /*      Report Count (8),               */
0x81, 0x02,         /*      Input (Variable),               */
0x95, 0x01,         /*      Report Count (1),               */
0x75, 0x08,         /*      Report Size (8),                */
0x81, 0x01,         /*      Input (Constant),               */
0x95, 0x03,         /*      Report Count (3),               */
0x75, 0x01,         /*      Report Size (1),                */
0x05, 0x08,         /*      Usage Page (LED),               */
0x19, 0x01,         /*      Usage Minimum (01h),            */
0x29, 0x03,         /*      Usage Maximum (03h),            */
0x91, 0x02,         /*      Output (Variable),              */
0x95, 0x05,         /*      Report Count (5),               */
0x75, 0x01,         /*      Report Size (1),                */
0x91, 0x01,         /*      Output (Constant),              */
0x95, 0x06,         /*      Report Count (6),               */
0x75, 0x08,         /*      Report Size (8),                */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
0x05, 0x07,         /*      Usage Page (Keyboard),          */
0x19, 0x00,         /*      Usage Minimum (None),           */
0x2A, 0xFF, 0x00,   /*      Usage Maximum (FFh),            */
0x81, 0x00,         /*      Input,                          */
0xC0                /*  End Collection                      */

@waveform80
Copy link

sorry @probonopd - looks like you'd lose that bet :)

@probonopd
Copy link
Author

probonopd commented Sep 6, 2019

Thanks @waveform80.

So we have established that the usbhid-dump output is identical between languages.

The British Keyboard has iProduct RPI Wired Keyboard 1 (funny that being a UK company, they started with British rather than US as "1"). I am wondering whether they are just (ab)using the last digit in iProduct or whether more is going on... after all they were talking about "custom firmware for the key matrix management"...

I think this kind if thing should be universally standardized so that all keyboards and all OSes can use it.

@zyga
Copy link

zyga commented Sep 8, 2019

From the US layout:

zyga@pi0w-1:~ $ sudo lsusb -v -d 0x04d9:0x0006

Bus 001 Device 003: ID 04d9:0006 Holtek Semiconductor, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d9 Holtek Semiconductor, Inc.
  idProduct          0x0006 
  bcdDevice            1.10
  iManufacturer           1  
  iProduct                2 RPI Wired Keyboard 4
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x003b
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      65
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      59
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0005  1x 5 bytes
        bInterval              10
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)

the other one:

zyga@pi0w-1:~ $ sudo usbhid-dump -d 0x04d9:0x0006 | grep -v : | xxd -r -p | hidrd-convert -o code
0x05, 0x0C,         /*  Usage Page (Consumer),              */
0x09, 0x01,         /*  Usage (Consumer Control),           */
0xA1, 0x01,         /*  Collection (Application),           */
0x05, 0x0C,         /*      Usage Page (Consumer),          */
0x75, 0x01,         /*      Report Size (1),                */
0x95, 0x01,         /*      Report Count (1),               */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x25, 0x01,         /*      Logical Maximum (1),            */
0x09, 0xCD,         /*      Usage (Play Pause),             */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xB5,         /*      Usage (Scan Next Track),        */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xB6,         /*      Usage (Scan Previous Track),    */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xB8,         /*      Usage (Eject),                  */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xE2,         /*      Usage (Mute),                   */
0x81, 0x06,         /*      Input (Variable, Relative),     */
0x09, 0xEA,         /*      Usage (Volume Dec),             */
0x81, 0x02,         /*      Input (Variable),               */
0x09, 0xE9,         /*      Usage (Volume Inc),             */
0x81, 0x02,         /*      Input (Variable),               */
0x0A, 0x23, 0x02,   /*      Usage (AC Home),                */
0x81, 0x02,         /*      Input (Variable),               */
0x0A, 0x92, 0x01,   /*      Usage (AL Calculator),          */
0x81, 0x02,         /*      Input (Variable),               */
0x95, 0x07,         /*      Report Count (7),               */
0x81, 0x01,         /*      Input (Constant),               */
0xC0,               /*  End Collection,                     */
0x05, 0x01,         /*  Usage Page (Desktop),               */
0x09, 0x06,         /*  Usage (Keyboard),                   */
0xA1, 0x01,         /*  Collection (Application),           */
0x05, 0x07,         /*      Usage Page (Keyboard),          */
0x19, 0xE0,         /*      Usage Minimum (KB Leftcontrol), */
0x29, 0xE7,         /*      Usage Maximum (KB Right GUI),   */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x25, 0x01,         /*      Logical Maximum (1),            */
0x75, 0x01,         /*      Report Size (1),                */
0x95, 0x08,         /*      Report Count (8),               */
0x81, 0x02,         /*      Input (Variable),               */
0x95, 0x01,         /*      Report Count (1),               */
0x75, 0x08,         /*      Report Size (8),                */
0x81, 0x01,         /*      Input (Constant),               */
0x95, 0x03,         /*      Report Count (3),               */
0x75, 0x01,         /*      Report Size (1),                */
0x05, 0x08,         /*      Usage Page (LED),               */
0x19, 0x01,         /*      Usage Minimum (01h),            */
0x29, 0x03,         /*      Usage Maximum (03h),            */
0x91, 0x02,         /*      Output (Variable),              */
0x95, 0x05,         /*      Report Count (5),               */
0x75, 0x01,         /*      Report Size (1),                */
0x91, 0x01,         /*      Output (Constant),              */
0x95, 0x06,         /*      Report Count (6),               */
0x75, 0x08,         /*      Report Size (8),                */
0x15, 0x00,         /*      Logical Minimum (0),            */
0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
0x05, 0x07,         /*      Usage Page (Keyboard),          */
0x19, 0x00,         /*      Usage Minimum (None),           */
0x2A, 0xFF, 0x00,   /*      Usage Maximum (FFh),            */
0x81, 0x00,         /*      Input,                          */
0xC0                /*  End Collection                      */

@probonopd
Copy link
Author

probonopd commented Sep 8, 2019

RPI Wired Keyboard 4! Identical hidrd.

@l1m3r
Copy link

l1m3r commented Nov 26, 2019

Disclaimer: didn't read the whole thread.

SUN thinclients were able to automatically detect the language/layout of attached (old) SUN keyboards.
AFAIK the USB standard actually supports this functionality and it seems bCountryCode is actually the proper flag for that (see here).

I still have one or two of those keyboards and if you want I can see what I can figure out about them with UsbTreeView.

@probonopd
Copy link
Author

Interestingly, the Raspberry Pi keyboard does not seem to use bCountryCode?!

@trejan
Copy link

trejan commented Jun 7, 2020

The automatic configuration mechanism used isn't portable as it is part of their piwiz first-run setup tool. It looks for a USB device with VID:PID 04D9:0006 and a product description which includes "RPI Wired Keyboard". The number appended to the end of that description is an index into an array of keyboard layouts in piwiz.

@probonopd
Copy link
Author

Thanks for posting @trejan. Too bad there is no universally used standard for this.

Imho, a USB keyboard should have descriptors that describe what every key has printed on it. For example, if I want to make a keyboard in with totally custom printing on it, like "ABCDF", then it shoud be able to tell the system what the keys mean.

I fail to understand why this has to be so complicated.

@probonopd
Copy link
Author

probonopd commented Oct 11, 2020

Relevant. It seems that keyboard layout, system language, and timezone are derived based on the integer at the end of USB iProduct.

This means that as long as a USB iProduct RPI Wired Keyboard <integer> is attached to the computer, we may be able to automatically set keyboard layout, system language, and timezone.

https://github.com/raspberrypi-ui/piwiz/blob/861eb61156d4fc83c1871ec427cf806132e55ad2/src/piwiz.c#L226-L279

#define MAX_KBS 15
static const char kb_countries[MAX_KBS][3] = {
    "GB",   // default if no Pi keyboard found
    "GB",
    "FR",
    "ES",
    "US",
    "DE",
    "IT",
    "JP",
    "PT",
    "NO",
    "SE",
    "DK",
    "RU",
    "TR",
    "IL"
};

static const char kb_langs[MAX_KBS][3] = {
    "en",   // default if no Pi keyboard found
    "en",
    "fr",
    "es",
    "en",
    "de",
    "it",
    "jp",
    "pt",
    "nn",
    "se",
    "fi",
    "ru",
    "tr",
    "he"
};

static const char kb_tzs[MAX_KBS][20] = {
    "Europe/London",    // default if no Pi keyboard found
    "Europe/London",
    "Europe/Paris",
    "Europe/Madrid",
    "America/New_York",
    "Europe/Berlin",
    "Europe/Rome",
    "Asia/Tokyo",
    "Europe/Lisbon",
    "Europe/Oslo",
    "Europe/Stockholm",
    "Europe/Helsinki",
    "Europe/Moscow",
    "Europe/Istanbul",
    "Europe/Jerusalem"
};

@probonopd
Copy link
Author

probonopd commented Oct 11, 2020

This is how I interpret this:

RPI Wired Keyboard <integer> Country Language Assumed timezone
None (default) GB en Europe/London
1 GB en Europe/London
2 FR fr Europe/Paris
3 ES es Europe/Madrid
4 US en America/New_York
5 DE de Europe/Berlin
6 IT it Europe/Rome
7 JP jp Asia/Tokyo
8 PT pt Europe/Lisbon
9 NO nn Europe/Oslo
10 SE se Europe/Stockholm
11 DK fi Europe/Helsinki
12 RU ru Europe/Moscow
13 TR tr Europe/Istanbul
14 IL he Europe/Jerusalem

@probonopd
Copy link
Author

@probonopd
Copy link
Author

https://github.com/helloSystem configures the keyboard layout, system language, and timezone automatically if it detects a Raspberry Pi keyboard at boot.

@probonopd
Copy link
Author

raspberrypi-ui/piwiz#15 (comment)

For a Pi 400 keyboard, the value in /proc/device-tree/chosen/rpi-country-code is checked. Have a look at the get_pi_keyboard function in the source code for piwiz.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment