PaulStoffregen / USBHost_t36

USB Host Library for Teensy 3.6 and 4.0
170 stars 87 forks source link

Support for Apple Magic Keyboard A1843 CAPS LED #138

Open peterburk opened 1 month ago

peterburk commented 1 month ago

Thank you for your library! I just got a Teensy 4.0, and was able to use the MouseKeyboardForward.ino sketch with a Dell keyboard right out of the box.

My project is to build an HID remapper for my Apple Magic Keyboard A1843, so I can type Command-C and have the office PC receive Ctrl-C (among other features, such as media keys sending UART commands to my iPod).

For the Magic Keyboard, it didn't work when I first plugged it in. I investigated further, and made the following changes to support typing and modifier keys.

C:\Users\burkipe\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.59.0\libraries\USBHost_t36\keyboard.cpp

uint8_t key_modifiers = 0;
uint32_t thisKey = 0;

bool KeyboardController::hid_process_in_data(const Transfer_t *transfer)
{
    const uint8_t *buffer = (const uint8_t *)transfer->buffer;

    uint16_t len = transfer->length;
    const uint8_t *p = buffer;
    USBHDBGSerial.printf("HPID(%p, %u):", transfer->driver, len);
      if (len > 32) len = 32;
    while (len--) USBHDBGSerial.printf(" %02X", *p++); 

    if (buffer[3] != thisKey)  { Keyboard.release(0XF000 | thisKey); thisKey = 0; }
    if (buffer[3] != 0x00) { thisKey = buffer[3]; Keyboard.press(0XF000 | thisKey);  }
    // if (buffer[3] == 0x00)  { Keyboard.release(0XF000 | thisKey); thisKey = 0; }

    if (buffer[3] == 0x39) 
    { 
        // leds_.capsLock = true; updateLEDS(); 
        if (driver_[0] != nullptr) {
                USBHDBGSerial.printf("driver_[0]->sendControlPacket");
            // Only do it this way if we are a standard USB device
            leds_.capsLock = true;
        driver_[0]->sendControlPacket(0x21, 9, 0x200, 1, sizeof(leds_.byte), (void*) &leds_.byte); 
            // sendControlPacket(uint32_t bmRequestType, uint32_t bRequest, uint32_t wValue, uint32_t wIndex, uint32_t wLength, void *buf)

            } else {
                USBHDBGSerial.printf("driver_[0] is nullptr");
            }
    }

    // Probably need to do some more checking of the data, but
    // first pass if length == 8 assume boot format:
    // Hoped driver would be something I could check but...
    if ((transfer->driver == driver_[0]) &&  (transfer->length == 8)) {
        /*USBHDBGSerial.printf(" (boot)\n"); */
        process_boot_keyboard_format(buffer, true);
        keyboard_uses_boot_format_  = true;
        return true;
    }
    USBHDBGSerial.printf("\n");

    return false;
}

void KeyboardController::hid_input_data(uint32_t usage, int32_t value)
{
    // Hack ignore 0xff00 high words as these are user values... 
    // USBHDBGSerial.printf("KeyboardController: topusage= %x usage=%X, value=%d\n", topusage_, usage, value);

    if ((usage & 0xffff0000) == 0xff000000) return; 

    if ((topusage_ == 0x10000 ) && (usage == 0x700E1) && (value == 1)) {        USBHDBGSerial.printf("Left shift down"); int mask = 1 << 1; key_modifiers |= mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);   }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E1) && (value == 0)) {        USBHDBGSerial.printf("Left shift up"); int mask = 1 << 1; key_modifiers &= ~mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);    }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E3) && (value == 1)) {        USBHDBGSerial.printf("Command down"); int mask = 1 << 3; key_modifiers |= mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);  }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E3) && (value == 0)) {        USBHDBGSerial.printf("Command up"); int mask = 1 << 3; key_modifiers &= ~mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);   }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E0) && (value == 1)) {        USBHDBGSerial.printf("Control down"); int mask = 1 << 0; key_modifiers |= mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);  }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E0) && (value == 0)) {        USBHDBGSerial.printf("Control up"); int mask = 1 << 0; key_modifiers &= ~mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);   }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E2) && (value == 1)) {        USBHDBGSerial.printf("Alt down"); int mask = 1 << 2; key_modifiers |= mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);  }
    if ((topusage_ == 0x10000 ) && (usage == 0x700E2) && (value == 0)) {        USBHDBGSerial.printf("Alt up"); int mask = 1 << 2; key_modifiers &= ~mask; USBHDBGSerial.printf("key_modifiers %x", key_modifiers); Keyboard.set_modifier(key_modifiers);   }

    // If this is the TOPUSAGE_KEYBOARD do in it's own function
    if (process_hid_keyboard_data(usage, value))
        return;

Now I'm trying to support the CAPS lock LED.

TinyUSB is able to switch on the LED correctly using a Raspberry Pi Pico, by sending the following commands:

image

On the Teensy,

driver_[0]->sendControlPacket(0x21, 9, 0x200, 1, sizeof(leds_.byte), (void*) &leds_.byte); 

is sending correctly, but the second IN transaction is STALLing.

image

How do I ACK the IN transaction from the keyboard?