tmk_keyboard/converter/adb_usb/matrix.c
tmk 0c25a46dc8 adb_usb: Fix for weirdness of 2-button mouses #724
This ignores 'optional second button' in Apple Classic Mouse protocol
as Mac OS9 and OSX does. This is needed and useful in most cases.
NeXT ADB Mouse is an exception, this disables its right button.
You can disable this using `ADB_MOUSE_2ND_BUTTON_QUIRK` in config.h.
2022-04-11 22:18:01 +09:00

761 lines
28 KiB
C

/*
Copyright 2011 Jun Wako <wakojun@gmail.com>
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.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* scan matrix
*/
#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include "print.h"
#include "util.h"
#include "debug.h"
#include "adb.h"
#include "matrix.h"
#include "report.h"
#include "host.h"
#include "led.h"
#include "timer.h"
#include "wait.h"
static bool has_media_keys = false;
static bool is_iso_layout = false;
#if ADB_MOUSE_ENABLE
#define dmprintf(fmt, ...) do { /* if (debug_mouse) */ xprintf("M:" fmt, ##__VA_ARGS__); } while (0)
static uint16_t mouse_cpi = 100;
static void mouse_init(void);
#endif
// matrix state buffer(1:on, 0:off)
static matrix_row_t matrix[MATRIX_ROWS];
static void register_key(uint8_t key);
static void device_scan(void)
{
xprintf("\nScan:\n");
for (uint8_t addr = 0; addr < 16; addr++) {
uint16_t reg3 = adb_host_talk(addr, ADB_REG_3);
if (reg3) {
xprintf(" addr:%d, reg3:%04X\n", addr, reg3);
}
}
xprintf("\n");
}
static void keyboard_init(void)
{
uint16_t reg3;
uint8_t handler;
// Check if there is keyboard at default address
reg3 = adb_host_talk(ADB_ADDR_KEYBOARD, ADB_REG_3);
if (!reg3) return;
if (reg3) {
xprintf("K:found: addr:" xstr(ADB_ADDR_KEYBOARD) " reg3:%04X\n", reg3);
adb_host_listen(ADB_ADDR_KEYBOARD, ADB_REG_3, ((reg3 >> 8) & 0xF0) | ADB_ADDR_KBD_TMP, 0xFE);
}
// Check if there is device to setup at temporary address
reg3 = adb_host_talk(ADB_ADDR_KBD_TMP, ADB_REG_3);
if (!reg3) {
xprintf("K:fail: move\n");
return;
}
// Determine ISO keyboard by handler id
// http://lxr.free-electrons.com/source/drivers/macintosh/adbhid.c?v=4.4#L815
handler = reg3 & 0xFF;
switch (handler) {
case 0x04: case 0x05: case 0x07: case 0x09: case 0x0D:
case 0x11: case 0x14: case 0x19: case 0x1D: case 0xC1:
case 0xC4: case 0xC7:
is_iso_layout = true;
break;
default:
is_iso_layout = false;
break;
}
// Adjustable keyboard media keys: address=0x07 and handlerID=0x02
has_media_keys = (0x02 == (adb_host_talk(ADB_ADDR_APPLIANCE, ADB_REG_3) & 0xff));
if (has_media_keys) {
xprintf("K:Media keys\n");
}
// Enable keyboard left/right modifier distinction
// Listen Register3
// upper byte: reserved bits 0000, keyboard address 0010
// lower byte: device handler 00000011
adb_host_listen(ADB_ADDR_KBD_TMP, ADB_REG_3, ADB_ADDR_KBD_TMP, ADB_HANDLER_EXTENDED_KEYBOARD);
reg3 = adb_host_talk(ADB_ADDR_KBD_TMP, ADB_REG_3);
adb_host_kbd_led(ADB_ADDR_KBD_TMP, ~(host_keyboard_leds()));
// Move to keyboard polling address
adb_host_listen(ADB_ADDR_KBD_TMP, ADB_REG_3, ((reg3 >> 8) & 0xF0) | ADB_ADDR_KBD_POLL, 0xFE);
if (adb_host_talk(ADB_ADDR_KBD_TMP, ADB_REG_3)) {
xprintf("K:fail: move\n");
}
xprintf("K:setup: addr:" xstr(ADB_ADDR_KBD_POLL) " reg3:%04X, ISO:%s\n",
reg3, (is_iso_layout ? "yes" : "no"));
}
void matrix_init(void)
{
debug_enable = true;
//debug_matrix = true;
//debug_keyboard = true;
//debug_mouse = true;
// LED on
DDRD |= (1<<6); PORTD |= (1<<6);
adb_host_init();
adb_host_reset_hard();
//adb_host_reset(); // some of devices done't recognize
// AEK/AEKII(ANSI/ISO) startup is slower. Without proper delay
// it would fail to recognize layout and enable Extended protocol.
// 200ms seems to be enough for AEKs. 1000ms is used for safety.
// Tested with devices:
// M0115J(AEK), M3501(AEKII), M0116(Standard), M1242(Adjustable),
// G5431(Mouse), 64210(Kensington Trubo Mouse 5)
wait_ms(1000);
device_scan();
// initialize matrix state: all keys off
for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
led_set(host_keyboard_leds());
// LED off
DDRD |= (1<<6); PORTD &= ~(1<<6);
return;
}
#ifdef ADB_MOUSE_ENABLE
static uint8_t mouse_handler;
static void mouse_init(void)
{
uint16_t reg3;
again:
// Check if there is mouse device at default address 3
reg3 = adb_host_talk(ADB_ADDR_MOUSE, ADB_REG_3);
if (reg3) {
// Move device to tmp address
// Collision detection can fail sometimes in fact when two devices are connected on startup
// and the devices can be moved to tmp address at same time in the result. In that case
// initialization of mouse can fail. To recover this you may have to replug mouse or converter.
// It is safe to have just one mouse device, but more than one device can be handled somehow.
adb_host_flush(ADB_ADDR_MOUSE);
adb_host_listen(ADB_ADDR_MOUSE, ADB_REG_3, ((reg3 >> 8) & 0xF0) | ADB_ADDR_MOUSE_TMP, 0xFE);
adb_host_flush(ADB_ADDR_MOUSE_TMP);
}
// Check if there is mouse device to setup at temporary address 15
reg3 = adb_host_talk(ADB_ADDR_MOUSE_TMP, ADB_REG_3);
if (!reg3) {
return;
}
dmprintf("TMP: reg3:%04X\n", reg3);
mouse_handler = reg3 & 0xFF;
if (mouse_handler == ADB_HANDLER_MICROSPEED_MACTRAC ||
mouse_handler == ADB_HANDLER_MICROSPEED_UNKNOWN ||
mouse_handler == ADB_HANDLER_CONTOUR_MOUSE ||
mouse_handler == ADB_HANDLER_CHPRODUCTS_PRO) {
// https://github.com/NetBSD/src/blob/netbsd-9/sys/arch/macppc/dev/ams.c#L226-L255
// https://github.com/torvalds/linux/blob/v5.17/drivers/macintosh/adbhid.c#L1007-L1018
// https://github.com/torvalds/linux/blob/v5.17/drivers/macintosh/adbhid.c#L1204-L1239
uint8_t cmd[] = { 0x00, // alt speed max
0x00, // speed max
0x10, // ext protocol enabled
0x07 }; // buttons without locking
//adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen_buf(ADB_ADDR_MOUSE_TMP, ADB_REG_1, cmd, sizeof(cmd));
}
// Try to escalate into extended/classic2 protocol
if (mouse_handler == ADB_HANDLER_CLASSIC1_MOUSE || mouse_handler == ADB_HANDLER_CLASSIC2_MOUSE) {
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_3, (reg3 >> 8), ADB_HANDLER_EXTENDED_MOUSE);
mouse_handler = (reg3 = adb_host_talk(ADB_ADDR_MOUSE_TMP, ADB_REG_3)) & 0xFF;
if (mouse_handler != ADB_HANDLER_EXTENDED_MOUSE) {
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_3, (reg3 >> 8), ADB_HANDLER_MOUSESYSTEMS_A3);
mouse_handler = (reg3 = adb_host_talk(ADB_ADDR_MOUSE_TMP, ADB_REG_3)) & 0xFF;
if (mouse_handler == ADB_HANDLER_MOUSESYSTEMS_A3) {
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_2, 0x00, 0x07);
}
}
if (mouse_handler == ADB_HANDLER_CLASSIC1_MOUSE) {
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_3, (reg3 >> 8), ADB_HANDLER_CLASSIC2_MOUSE);
mouse_handler = (reg3 = adb_host_talk(ADB_ADDR_MOUSE_TMP, ADB_REG_3)) & 0xFF;
}
dmprintf("EXT: reg3:%04X\n", reg3);
}
// Classic Protocol 100cpi
if (mouse_handler == ADB_HANDLER_CLASSIC1_MOUSE) {
dmprintf("Classic 100cpi\n");
mouse_cpi = 100;
}
// Classic Protocol 200cpi
if (mouse_handler == ADB_HANDLER_CLASSIC2_MOUSE) {
dmprintf("Classic 200cpi\n");
mouse_cpi = 200;
}
// Extended Mouse Protocol
if (mouse_handler == ADB_HANDLER_EXTENDED_MOUSE) {
// Device info format(reg1 8-byte data)
// 0-3: device id
// 4-5: resolution in units/inch (0xC8=200upi)
// 6 : device class (0: Tablet, 1: Mouse, 2: Trackball)
// 7 : num of buttons
uint8_t len;
uint8_t buf[8];
len = adb_host_talk_buf(ADB_ADDR_MOUSE_TMP, ADB_REG_1, buf, sizeof(buf));
if (len > 5) {
mouse_cpi = (buf[4]<<8) | buf[5];
} else {
mouse_cpi = 100;
}
if (len) {
dmprintf("EXT: [%02X %02X %02X %02X %02X %02X %02X %02X] cpi=%d btn=%d len=%d\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], mouse_cpi, buf[7], len);
}
// Kensington Turbo Mouse 5: default device
if (buf[0] == 0x4B && buf[1] == 0x4D && buf[2] == 0x4C && buf[3] == 0x31) {
dmprintf("TM5: found\n");
// Move it to addr0 to remove this device and get new device with handle id 50 on addr 3
// and the new device on address 3 should be handled with command sequence later.
//
// Turbo Mouse 5 has one default device on addr3 as normal mouse at first, and another device
// with hander id 50 appears after the default device is moved from addr3.
// The mouse has the two devices at same time transiently in the result. The default device is
// removed automatically after the another device receives command sequence.
// NOTE: The mouse hangs if you try moving the two deivces to same address.
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_3, ((reg3 >> 8) & 0xF0) | ADB_ADDR_0, 0xFE);
goto again;
} else if (buf[0] == 0x4B && buf[1] == 0x4F && buf[2] == 0x49 && buf[3] == 0x54) {
// https://elixir.bootlin.com/linux/v5.17/source/drivers/macintosh/adbhid.c#L1068
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_3, (reg3 >> 8), ADB_HANDLER_MACALLY2_MOUSE);
mouse_handler = (reg3 = adb_host_talk(ADB_ADDR_MOUSE_TMP, ADB_REG_3)) & 0xFF;
xprintf("M: reg3:%04X\n", reg3);
dmprintf("Macally2: found: %02X\n", mouse_handler);
} else if (buf[0] == 0x9A && (buf[1] == 0x20 || buf[1] == 0x21)) {
if (buf[1] == 0x20) {
xprintf("M:MouseMan\n");
} else {
xprintf("M:TrackMan\n");
}
// https://elixir.bootlin.com/linux/v5.17/source/drivers/macintosh/adbhid.c#L1047
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_1, 0x00, 0x81);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_1, 0x01, 0x81);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_1, 0x02, 0x81);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_1, 0x03, 0x38);
// set pseudo handler for Logitech
mouse_handler = ADB_HANDLER_LOGITECH;
} else if (buf[0] == 0x4C && buf[1] == 0x54) {
// Logitech Extended
// MouseMan - FCCID:DZLMAH32 'LT01'
// MouseMan Cordless - FCCID:DZLMRC33T 'LTW1'
xprintf("M:Logitech-Ext\n");
// set pseudo handler
mouse_handler = ADB_HANDLER_LOGITECH_EXT;
} else {
dmprintf("Extended\n");
}
}
// Kensington Turbo Mouse 5: setup
if (mouse_handler == ADB_HANDLER_TURBO_MOUSE) {
xprintf("TM5: setup\n");
/* byte0: 0xb5 speed - 0xa0, 0xa5, 0xb0 and 0xb5 seem to work
* uppper nibble:
* 0x00-70, 0xc0-f0 no movement and button event
* 0x80 enables mouse output speed slow
* 0x90 enables mouse output
* 0xa0 enables mouse output
* 0xb0 enables mouse output speed fast -126 to 126
* lower nibble:
* 0x08 makes cursor not smooth, bit3 should be 0
* 0x02 disables button4, bit1 should be 0
* how other bits work is not clear.
* byte1: 0x14 button mapping - upper nibble for button1 and lower for button2
* button1 and button2 mapped as themselves
* 0x0 disabled
* 0x1 button1
* 0x2 button1 toggle
* 0x3 no effect key event FFFF
* 0x4 button2
* 0x5 button2 toggle
* 0x6 button3
* 0x7 button3 toggle
* 0x8 ?toggle weirdly?
* 0x9 button4
* 0xa button4 toggle
* 0xb ?disabled?
* 0xc Left
* 0xd Right
* 0xe Alt+Left
* 0xf Alt+Right
* byte2: 0x00 - 0x40 on powerup, seems to do nothing
* byte3: 0x00 - 0x01 on powerup, seems to do nothing
* byte4: 0x69 button mapping - upper nibble for button3 and lower for button4
* button3 and button4 mapped as themselves(see byte1)
* byte5: 0xff unknown
* byte6: 0xff unknown
* byte7: 0x?? checksum
* byte7 = byte0 ^ byte1 ^ byte2 ^ byte3 ^ byte4 ^ byte5 ^ byte6 ^ 0xFF;
* https://github.com/NetBSD/src/blob/8966d5b1cf335756dd9bba3331e84c659bf917e1/sys/dev/adb/adb_ktm.c#L181
*/
uint8_t cmd[] = { 0xB5, 0x14, 0x00, 0x00, 0x69, 0xFF, 0xFF, 0x37 };
// cmd[7] = cmd[0] ^ cmd[1] ^ cmd[2] ^ cmd[3] ^ cmd[4] ^ cmd[5] ^ cmd[6] ^ 0xFF;
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen_buf(ADB_ADDR_MOUSE_TMP, ADB_REG_2, cmd, sizeof(cmd));
}
// Move to address 10 for mouse polling
adb_host_flush(ADB_ADDR_MOUSE_TMP);
adb_host_listen(ADB_ADDR_MOUSE_TMP, ADB_REG_3, ((reg3 >> 8) & 0xF0) | ADB_ADDR_MOUSE_POLL, 0xFE);
adb_host_flush(ADB_ADDR_MOUSE_POLL);
reg3 = adb_host_talk(ADB_ADDR_MOUSE_TMP, ADB_REG_3);
if (reg3) {
dmprintf("POL: fail reg3:%04X\n", reg3);
} else {
dmprintf("POL: done\n");
}
device_scan();
return;
}
static report_mouse_t mouse_report = {};
static int32_t scroll_state = 0;
static uint8_t scroll_speed = ADB_MOUSE_SCROLL_SPEED;
static uint8_t scroll_button_mask = (1 << ADB_MOUSE_SCROLL_BUTTON) >> 1;
void adb_mouse_task(void)
{
uint8_t len;
uint8_t buf[5];
int16_t x, y;
static int8_t mouseacc;
/* tick of last polling */
static uint16_t tick_ms;
// polling with 12ms interval
if (timer_elapsed(tick_ms) < 12) return;
tick_ms = timer_read();
static uint16_t detect_ms;
if (timer_elapsed(detect_ms) > 1000) {
detect_ms = timer_read();
// check new device on addr3
mouse_init();
}
len = adb_host_talk_buf(ADB_ADDR_MOUSE_POLL, ADB_REG_0, buf, sizeof(buf));
// If nothing received reset mouse acceleration, and quit.
if (len < 2) {
mouseacc = 1;
return;
};
xprintf("M:[ ");
for (uint8_t i = 0; i < len; i++)
xprintf("%02X ", buf[i]);
xprintf("] mh:%02X\n", mouse_handler);
bool xneg = false;
bool yneg = false;
if (mouse_handler == ADB_HANDLER_LOGITECH) {
// Logitech:
// Byte0: bbb y06 y05 y04 y03 y02 y01 y00
// Byte1: 1 x06 x05 x04 x03 x02 x01 x00
// Byte2: 0 0 0 0 0 BL BM BR
// Bx: button state(1:pressed, 1:released)
// bbb: 0 when either BL, BR or BM is pressed
if (buf[0] & 0x40) yneg = true;
if (buf[1] & 0x40) xneg = true;
if (buf[2] & 0x04) buf[0] &= 0x7F; else buf[0] |= 0x80;
if (buf[2] & 0x01) buf[1] &= 0x7F; else buf[1] |= 0x80;
if (buf[2] & 0x02) buf[2] = 0x08; else buf[2] = 0x88;
if (yneg) buf[2] |= 0x70;
if (xneg) buf[2] |= 0x07;
len = 3;
} else if (mouse_handler == ADB_HANDLER_LOGITECH_EXT) {
// Logitech Extended:
// Byte0: b00 y06 y05 y04 y03 y02 y01 y00
// Byte1: b02 x06 x05 x04 x03 x02 x01 x00
// Byte2: b01 y09 y08 y07 b03 x09 x08 x07
// L=b00, R=b01, M=b02
uint8_t tmp = buf[2];
if (buf[1] & 0x80) buf[2] |= 0x80; else buf[2] &= 0x7F;
if (tmp & 0x80) buf[1] |= 0x80; else buf[1] &= 0x7F;
if (buf[len - 1] & 0x40) yneg = true;
if (buf[len - 1] & 0x04) xneg = true;
} else if (mouse_handler == ADB_HANDLER_MACALLY2_MOUSE && len == 4) {
// Macally 2-button mouse:
// Byte0: b00 y06 y05 y04 y03 y02 y01 y00
// Byte1: b01 x06 x05 x04 x03 x02 x01 x00
// Byte2: 1 0 0 0 1 0 0 0
// Byte3: 1 0 0 0 1 0 0 0
// b--: button state(0:pressed, 1:released)
if (buf[0] & 0x40) yneg = true;
if (buf[1] & 0x40) xneg = true;
// Ignore Byte2 and 3
len = 2;
} else if (mouse_handler == ADB_HANDLER_MICROSPEED_MACTRAC ||
mouse_handler == ADB_HANDLER_MICROSPEED_UNKNOWN ||
mouse_handler == ADB_HANDLER_CONTOUR_MOUSE) {
// Microspeed:
// Byte0: ??? y06 y05 y04 y03 y02 y01 y00
// Byte1: ??? x06 x05 x04 x03 x02 x01 x00
// Byte2: ??? ??? ??? ??? ??? bM bR bL
// Contour Mouse:
// Byte0: bbb y06 y05 y04 y03 y02 y01 y00
// Byte1: 1 x06 x05 x04 x03 x02 x01 x00
// Byte2: 0 0 0 0 1 bM bR bL
// Byte3: 0 0 0 0 1 bM bR bL
// b--: button state(0:pressed, 1:released)
if (buf[0] & 0x40) yneg = true;
if (buf[1] & 0x40) xneg = true;
buf[0] = ((buf[2] & 1) << 7) | (buf[0] & 0x7F);
buf[1] = ((buf[2] & 2) << 6) | (buf[1] & 0x7F) ;
buf[2] = ((buf[2] & 4) << 5) | (buf[2] & 8) | (yneg ? 0x70 : 0x00) | (xneg ? 0x07 : 0x00);
len = 3;
} else if (mouse_handler == ADB_HANDLER_CHPRODUCTS_PRO) {
// CH Products Trackball Pro:
// Byte0: ??? y06 y05 y04 y03 y02 y01 y00
// Byte1: ??? x06 x05 x04 x03 x02 x01 x00
// Byte2: ??? ??? ??? ??? bL0 bL1 bR bM
// b--: button state(0:pressed, 1:released)
// L=(bL0 & bL1)
if (buf[0] & 0x40) yneg = true;
if (buf[1] & 0x40) xneg = true;
buf[0] = (((buf[2] & 4) << 5) & ((buf[2] & 8) << 4)) | (buf[0] & 0x7F);
buf[1] = ((buf[2] & 2) << 6) | (buf[1] & 0x7F) ;
buf[2] = ((buf[2] & 1) << 7) | (yneg ? 0x70 : 0x00) | (xneg ? 0x0F : 0x08);
len = 3;
} else if (mouse_handler == ADB_HANDLER_MOUSESYSTEMS_A3) {
// Mouse Systems A3: 3-button mouse/trackball:
// Byte0: ??? y06 y05 y04 y03 y02 y01 y00
// Byte1: ??? x06 x05 x04 x03 x02 x01 x00
// Byte2: ??? ??? ??? ??? ??? bR bM bL
// b--: button state(0:pressed, 1:released)
if (buf[0] & 0x40) yneg = true;
if (buf[1] & 0x40) xneg = true;
buf[0] = ((buf[2] & 1) << 7) | (buf[0] & 0x7F);
buf[1] = ((buf[2] & 4) << 5) | (buf[1] & 0x7F) ;
buf[2] = ((buf[2] & 2) << 6) | (yneg ? 0x70 : 0x00) | (xneg ? 0x0F : 0x08);
len = 3;
} else if (mouse_handler == ADB_HANDLER_EXTENDED_MOUSE ||
mouse_handler == ADB_HANDLER_TURBO_MOUSE) {
// Apple Extended Mouse:
// Byte0: b00 y06 y05 y04 y03 y02 y01 y00
// Byte1: b01 x06 x05 x04 x03 x02 x01 x00
// Byte2: b02 y09 y08 y07 b03 x09 x08 x07
// Byte3: b04 y12 y11 y10 b05 x12 x11 x10
// Byte4: b06 y15 y14 y13 b07 x15 x14 x13
// b--: button state(0:pressed, 1:released)
// Data can be 2-5 bytes.
// L=b00, R=b01, M=b02
if (buf[len - 1] & 0x40) yneg = true;
if (buf[len - 1] & 0x04) xneg = true;
} else {
// Apple Classic Mouse and Unknown devices:
// Byte0: b00 y06 y05 y04 y03 y02 y01 y00
// Byte1: b01 x06 x05 x04 x03 x02 x01 x00
if (buf[0] & 0x40) yneg = true;
if (buf[1] & 0x40) xneg = true;
len = 2;
#ifdef ADB_MOUSE_2ND_BUTTON_QUIRK
// Ignore b01('optional second button') as OSX/MacOS9 does.
// Some mouses misuse the bit and make it unusable.
// https://github.com/tmk/tmk_keyboard/issues/724
buf[1] |= 0x80;
#endif
}
// Make unused buf bytes compatible with Extended Mouse Protocol
for (int8_t i = len; i < sizeof(buf); i++) {
buf[i] = 0x88;
if (yneg) buf[i] |= 0x70;
if (xneg) buf[i] |= 0x07;
}
xprintf("M:[ ");
for (uint8_t i = 0; i < sizeof(buf); i++)
xprintf("%02X ", buf[i]);
xprintf("]\n");
uint8_t buttons = 0;
if (!(buf[4] & 0x08)) buttons |= MOUSE_BTN8;
if (!(buf[4] & 0x80)) buttons |= MOUSE_BTN7;
if (!(buf[3] & 0x08)) buttons |= MOUSE_BTN6;
if (!(buf[3] & 0x80)) buttons |= MOUSE_BTN5;
if (!(buf[2] & 0x08)) buttons |= MOUSE_BTN4;
if (!(buf[2] & 0x80)) buttons |= MOUSE_BTN3; // Middle
if (!(buf[1] & 0x80)) buttons |= MOUSE_BTN2; // Right
if (!(buf[0] & 0x80)) buttons |= MOUSE_BTN1; // Left
// check if the scroll enable button is pressed
bool scroll_enable = (bool)(buttons & scroll_button_mask);
// mask out the scroll button so it isn't reported
buttons &= ~scroll_button_mask;
mouse_report.buttons = buttons;
int16_t xx, yy;
yy = (buf[0] & 0x7F) | (buf[2] & 0x70) << 3 | (buf[3] & 0x70) << 6 | (buf[4] & 0x70) << 9;
xx = (buf[1] & 0x7F) | (buf[2] & 0x07) << 7 | (buf[3] & 0x07) << 10 | (buf[4] & 0x07) << 13;
// Accelerate mouse. (They weren't meant to be used on screens larger than 320x200).
x = xx * mouseacc;
y = yy * mouseacc;
#ifndef MOUSE_EXT_REPORT
x = (x > 127) ? 127 : ((x < -127) ? -127 : x);
y = (y > 127) ? 127 : ((y < -127) ? -127 : y);
#endif
if (scroll_enable) {
scroll_state -= y;
mouse_report.v = scroll_state / scroll_speed;
scroll_state %= scroll_speed;
mouse_report.x = 0;
mouse_report.y = 0;
} else {
scroll_state = 0;
mouse_report.v = 0;
mouse_report.x = x;
mouse_report.y = y;
}
dmprintf("[B:%02X X:%d(%d) Y:%d(%d) V:%d A:%d]\n", mouse_report.buttons, mouse_report.x, xx, mouse_report.y, yy, mouse_report.v, mouseacc);
// Send result by usb.
host_mouse_send(&mouse_report);
// TODO: acceleration curve is needed for precise operation?
// increase acceleration of mouse
mouseacc += ( mouseacc < (mouse_cpi < 200 ? ADB_MOUSE_MAXACC : ADB_MOUSE_MAXACC/2) ? 1 : 0 );
return;
}
uint8_t adb_mouse_buttons(void)
{
return mouse_report.buttons;
}
#endif
uint8_t matrix_scan(void)
{
/* extra_key is volatile and more convoluted than necessary because gcc refused
to generate valid code otherwise. Making extra_key uint8_t and constructing codes
here via codes = extra_key<<8 | 0xFF; would consistently fail to even LOAD
extra_key from memory, and leave garbage in the high byte of codes. I tried
dozens of code variations and it kept generating broken assembly output. So
beware if attempting to make extra_key code more logical and efficient. */
static volatile uint16_t extra_key = 0xFFFF;
uint16_t codes;
uint8_t key0, key1;
/* tick of last polling */
static uint16_t tick_ms;
codes = extra_key;
extra_key = 0xFFFF;
if ( codes == 0xFFFF )
{
// polling with 12ms interval
if (timer_elapsed(tick_ms) < 12) return 0;
tick_ms = timer_read();
codes = adb_host_kbd_recv(ADB_ADDR_KBD_POLL);
if (codes) xprintf("%04X ", codes);
// Adjustable keybaord media keys
if (codes == 0 && has_media_keys &&
(codes = adb_host_kbd_recv(ADB_ADDR_APPLIANCE))) {
xprintf("m:%04X ", codes);
// key1
switch (codes & 0x7f ) {
case 0x00: // Mic
codes = (codes & ~0x007f) | 0x42;
break;
case 0x01: // Mute
codes = (codes & ~0x007f) | 0x4a;
break;
case 0x02: // Volume down
codes = (codes & ~0x007f) | 0x49;
break;
case 0x03: // Volume Up
codes = (codes & ~0x007f) | 0x48;
break;
case 0x7F: // no code
break;
default:
xprintf("ERROR: media key1\n");
return 0x11;
}
// key0
switch ((codes >> 8) & 0x7f ) {
case 0x00: // Mic
codes = (codes & ~0x7f00) | (0x42 << 8);
break;
case 0x01: // Mute
codes = (codes & ~0x7f00) | (0x4a << 8);
break;
case 0x02: // Volume down
codes = (codes & ~0x7f00) | (0x49 << 8);
break;
case 0x03: // Volume Up
codes = (codes & ~0x7f00) | (0x48 << 8);
break;
default:
xprintf("ERROR: media key0\n");
return 0x10;
}
}
}
key0 = codes>>8;
key1 = codes&0xFF;
if (codes == 0) { // no keys
static uint16_t detect_ms;
if (timer_elapsed(detect_ms) > 1000) {
detect_ms = timer_read();
// check new device on addr2
keyboard_init();
}
return 0;
} else if (codes == 0x7F7F) { // power key press
register_key(0x7F);
} else if (codes == 0xFFFF) { // power key release
register_key(0xFF);
} else {
// Macally keyboard sends keys inversely against ADB protocol
// https://deskthority.net/workshop-f7/macally-mk96-t20116.html
if (key0 == 0xFF) {
key0 = key1;
key1 = 0xFF;
}
/* Swap codes for ISO keyboard
* https://github.com/tmk/tmk_keyboard/issues/35
*
* ANSI
* ,----------- ----------.
* | *a| 1| 2 =|Backspa|
* |----------- ----------|
* |Tab | Q| | ]| *c|
* |----------- ----------|
* |CapsLo| A| '|Return |
* |----------- ----------|
* |Shift | Shift |
* `----------- ----------'
*
* ISO
* ,----------- ----------.
* | *a| 1| 2 =|Backspa|
* |----------- ----------|
* |Tab | Q| | ]|Retur|
* |----------- -----` |
* |CapsLo| A| '| *c| |
* |----------- ----------|
* |Shif| *b| Shift |
* `----------- ----------'
*
* ADB scan code USB usage
* ------------- ---------
* Key ANSI ISO ANSI ISO
* ---------------------------------------------
* *a 0x32 0x0A GRAVE GRAVE
* *b ---- 0x32 ---- NUBS
* *c 0x2A 0x70 BSLASH NUHS
*/
if (is_iso_layout) {
if ((key0 & 0x7F) == 0x32) {
key0 = (key0 & 0x80) | 0x0A;
} else if ((key0 & 0x7F) == 0x0A) {
key0 = (key0 & 0x80) | 0x32;
}
if ((key0 & 0x7F) == 0x2A) {
key0 = (key0 & 0x80) | 0x70;
}
}
register_key(key0);
if (key1 != 0xFF) // key1 is 0xFF when no second key.
extra_key = key1<<8 | 0xFF; // process in a separate call
}
return 1;
}
inline
matrix_row_t matrix_get_row(uint8_t row)
{
return matrix[row];
}
inline
static void register_key(uint8_t key)
{
uint8_t col, row;
col = key&0x07;
row = (key>>3)&0x0F;
if (key&0x80) {
matrix[row] &= ~(1<<col);
} else {
matrix[row] |= (1<<col);
}
}
void led_set(uint8_t usb_led)
{
adb_host_kbd_led(ADB_ADDR_KBD_POLL, ~usb_led);
}