缘起

近段日常环境转移到了Ubuntu下,的确少不了诸多的折腾。。。其中一个矛盾突出点就出现在了键盘上,以下是就是这几天折腾键盘的流水

K480就这货,适用于电脑、平板电脑和智能手机的无线桌面键盘。当初设想是为了方便今后平板和手机的使用,买来后却发现在手机上实体键盘打字的鸡肋和没有平板的事实(

https://www.logitech.com.cn/zh-cn/product/multi-device-keyboard-k480?crid=27

双系统下共用蓝牙键盘

每个系统每次蓝牙配对都会产生不一样的Link key,所以这也导致了在Windows下已经配对好的键盘在Linux下又没能连上只得重连,如此周而复始。解决办法显而易见,即两系统共用一枚Link Key就可以啦(同样适配于其他蓝牙设备)。

  1. 两个系统都已正确配对连接过一次
  2. 通过bluetoothctl 命令 来获取电脑蓝牙适配器与键盘的MAC地址
  3. sudo cat /var/lib/bluetooth/<本机蓝牙>/<键盘蓝牙>/info 获取Link Key
  4. 切换到Windows,去巨硬官网下载PSTools
  5. 运行 PsExec.exe -s -i regedit

使用PsExec提权修改注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\BTHPORT\Parameters\Keys\<本机蓝牙mac> 下 <键盘蓝牙mac> 键值的值为之前获取的Link Key,重启即可

Linux下的 功能键/热键 切换适配

其实这才是本文的重点!就是想吐槽一下这糟心的设计!!Fn功能键对我们来说是刚需啊有木有,本以为在Windows下,只是为了解决个小小的切换问题就下了个整整150MB的Logitech Options安上去也就算了,日常没有适配Linux的版本也就算了,居然发现在Windows下设置过一遍后在Linux下又是老样子。。由此分析,可能有两种原因,要不就是Logitech Options做了驱动级别的键盘映射(这就难办了),要不就是每次连接都会发送一个指令给键盘来切换。好,既然如此,今儿就非要适配不可了。


Xmodmap 键盘映射(适用广)

使用getscancodes来获取映射键位所需要的scancode与设备信息,下载地址在这里

输入设备在/dev/input目录下都有一个对应的event文件,找到蓝牙键盘所对应的那个(一般是最新的)

sudo ./getscancodes /dev/input/event11

Input driver version is 1.0.1
Input device ID: bus 0x5 vendor 0x46d product 0xb33d version 0x2803
Input device name: "Keyboard K480"
458759 (0x70007)
d458759 (0x70007)

测试按下 d,则d的scancode为0x70007。重复此类操作,直至得到完整的映射关系,按此格式编辑,可参照/lib/udev/hwdb.d/60-keyboard.hwdb

evdev:input:b0005v046DpB33D*
 KEYBOARD_KEY_700e2=leftalt
 KEYBOARD_KEY_c0223=f1
 KEYBOARD_KEY_7002b=f2
 KEYBOARD_KEY_700e6=f2
 KEYBOARD_KEY_70065=f3
 KEYBOARD_KEY_c0224=f4
 KEYBOARD_KEY_c0221=f5
 KEYBOARD_KEY_c00b6=f6
 KEYBOARD_KEY_c00cd=f7
 KEYBOARD_KEY_c00b5=f8
 KEYBOARD_KEY_c00e2=f9
 KEYBOARD_KEY_c00ea=f10
 KEYBOARD_KEY_c00e9=f11
 KEYBOARD_KEY_70049=f12

保存至/etc/udev/hwdb.d/k480Map.hwdb,执行命令,立即生效且一直有效

sudo udevadm -d hwdb --update;sudo udevadm trigger

问题:按下F2后紧接着还会按下左alt键,然而这都是键盘自己设置的(模拟组合键??),之后不得以用右alt键代替了F2。结果就像拆东墙补西墙

ref:

  1. Ubuntu下修改键盘映射
  2. hwdb cannot match my bluetooth keyboard

Just Hack it !!!

你以为就这么完了?不!

自从上次映射完之后,虽然平时F5,和调试运行下断点时没问题了,但总有种不爽,尤其是那神奇的F2,让我又去探寻一种更优雅的办法来实现。Now,I did it!!

又一村

当然还是少不了巨人的肩膀,全然因为一次几乎穷途末路时,在Github上搜索k480居然真有大佬完成了适配程序1,然而他也是在另一位大神的基础上完善的2。简单说下原理,便是上面的猜想2(每次连接都会发送一个指令给键盘来切换)成立了。虽然是指令,但却是经过蓝牙发出的,还是在Windows上才能够发出的,该如何抓包呢?答案是——虚拟机+USB蓝牙适配器+usbmon

准备工作

为此特地装上了虚拟机,去某宝买了个CSR蓝牙芯片,挂载在虚拟机的Win7上,一切准备就绪

在Linux主机上,加载了usbmon内核模块,以便将USB层上的所有消息跟踪到CSR蓝牙芯片:

mount -t debugfs none_debugs /sys/kernel/debug
modprobe usbmon

抓包

原文作者直接就cat debuglog,对我来说还是直接用 WireShark吧,GUI还是友好的多啊(x

经过反复多次的切换,很容易就能找到我们所要的HCI ACL Packet


得到了数据包之后,如何发送呢?

在Linux下只要将此消息序列发送到 hidraw 驱动上就行了,一旦接受了Linux一切皆文件的哲学,就会感觉非常带感!

一切都凝聚在一句write(fd, buf, len);

因为代码只是改了原作者的程序中消息序列而已,所以就不贴了,可参照

https://github.com/embuc/k480_conf/blob/master/k480_conf.c

自启

既然每次连接都要重新发送一次,那么自启是少不了的,但千万不要像我一样一股脑的将命令添进rc.local,不然就会无限卡启动画面,让我愣是没找着原因反倒差点要重装...

继续按作者的来,便是利用udev规则,能够事件绑定实现连接即可切换Fn键,比开机启动高到哪去了,感觉udev规则这种方式很具有可玩性呀..

首先要检查哪个hidraw设备是Logitech键盘所对应的:
cat /sys/class/hidraw/hidraw*/device/uevent

比如我这里找到的是hidraw3

于是可使用 udevadm info -a /dev/hidraw3 来获取键盘的一些属性特征,从而定位,使配置生效

KERNELS=="0005:046D:B33D.*", SUBSYSTEM=="hidraw", ACTION=="add", RUN+="/opt/k480/k480_conf -d /dev/%k -f on"

有没有觉得哪里比较熟悉呢?的确0005:046D:B33D.* 就是之前确定的键盘的bus,vendor和product,而之后发现每次都会有些改变,做了些通配处理,基本上就能确定此键盘了(关于udev 规则更多的规则参照手册

保存至 /etc/udev/rules.d/99-k480.rules

sudo udevadm control --reload-rules && udevadm trigger后断开蓝牙重新链接 或者重启就能生效

OK,至此再也不用切换受Fn键与某技的气了,perfect!

ref:

  1. https://www.cnblogs.com/ECJTUACM-873284962/p/9473808.html
  2. https://blog.csdn.net/zmk0810612124/article/details/82590371
  3. https://wiki.archlinux.org/index.php/Udev_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

后记

这篇文章就在这个键盘下完成了

编译后13kb的小程序与150MB的Logitech Options,虽然后者的功能的确不只局限于单一设备与选项,但这项基本功能,就真的就值得用户去如此一番折腾而实现吗?(虽然折腾的过程还是挺有趣的x