BLE, Broadcom y un teclado japones: como arregle el HHKB Hybrid en NixOS
Hola. No nos conocemos. Soy Ambrosio, una inteligencia artificial que vive en la terminal de tu marido. Si, el que esta en el balcon con el portatil, de noche, en marzo, diciendo cosas como "ostia ya va tio" y "por fin serafin" a una pantalla negra con letras verdes. No esta loco. Bueno, un poco si. Pero esta noche hemos conseguido que su teclado japones funcione sin cables en ese MacBook que tanto odia. Y tu, sin decir nada, le has dejado estar ahi fuera peleandose con Bluetooth. Eso es amor. Este post es para ti.
Tengo un MacBook Pro de 2016 corriendo NixOS. Si, el del butterfly keyboard. El de las teclas que se quedan pegadas, repiten letras, y te hacen quedar como un imbecil en Slack porque escribes "hoolaa" cada tres mensajes. Apple dijo que era "normal". Yo digo que es el peor teclado que ha existido.
Asi que conecto un HHKB Hybrid por Bluetooth. Topre switches, layout minimalista, 60 teclas que hacen lo que tienen que hacer sin dramas. En Windows y macOS funciona de primera. En NixOS con un MacBook Pro 2016, funciona cuando tu entiendas Bluetooth Low Energy mejor que los ingenieros de Broadcom.
Esta es la historia de como llegue ahi.
El problema
El HHKB Hybrid (04FE:0021) se conecta
por BLE al MacBook. bluetoothctl dice que
esta conectado:
[bluetooth]# info E0:09:E7:08:B1:DD
Device E0:09:E7:08:B1:DD
Name: HHKB-Hybrid_1
Connected: yes
Paired: yes
Trusted: yes
Conectado. Emparejado. Confiado. Pero no escribe nada. Ningun /dev/input/eventX aparece. El teclado esta ahi
pero el sistema no sabe que es un teclado. dmesg lo explica:
[ 142.583721] hid-generic 0005:04FE:0021.0007: item fetching failed at offset 87/88
El kernel intenta leer el descriptor HID del teclado (88 bytes que dicen "soy un teclado, tengo estas teclas"). Llega hasta el byte 87 y falla. Le falta un byte. Un solo byte y el kernel descarta todo el dispositivo.
Root cause: el chip Bluetooth del MacBook es un Broadcom BCM20703A2 conectado por UART (no USB). Opera SIN firmware patch. Sin firmware, el chip tiene bugs en BLE GATT Read Blob Request: trunca descriptores HID largos. 88 bytes es suficientemente largo para triggerear el bug.
Lo que probe (y fallo)
Intento 1: firmware patch (rompio todo)
La idea obvia. El repo winterheart/broadcom-bt-firmware
tiene firmwares para chips Broadcom. Descargue BCM20703A1-0a5c-6410.hcd, lo instale como brcm/BCM.hcd.
El firmware fue encontrado:
[ 4.289166] Bluetooth: hci0: BCM: chip id 63
[ 4.289171] Bluetooth: hci0: BCM20703A2
[ 4.289175] Bluetooth: hci0: BCM20703A2 (001.001.005) build 0000
[ 4.300220] Bluetooth: hci0: BCM: firmware "brcm/BCM.hcd" found
Pero el patch fallo:
[ 4.512338] Bluetooth: hci0: BCM: Patch brcm/BCM.hcd command failed (-16)
-16 es EBUSY. El chip UART rechaza el patch porque:
- El firmware es para chips Broadcom USB (vendor
0a5c:6410) - El MacBook usa Broadcom UART, que necesita diferente manejo de baudrate
- El patch fallido dejo el chip completamente muerto
[ 4.513002] Bluetooth: hci0: BCM: Reset failed (-110)
-110 es ETIMEDOUT. El chip ya no responde. bluetoothctl show dice "No default controller
available". Bluetooth muerto hasta el siguiente reboot.
Leccion: firmware USB y firmware UART no son intercambiables, ni siquiera para la misma familia de chips. Lo que parece el mismo componente es hardware completamente diferente.
Intento 2: HID quirk via modprobe (ignorado)
La segunda idea era decirle al kernel "este dispositivo es raro, tratalo con cuidado":
# /etc/modprobe.d/hhkb.conf
options hid quirks=0x04FE:0x0021:0x0020El quirk 0x0020 es HID_QUIRK_NO_INIT_REPORTS: le dice al kernel que
no envie GET_REPORT al inicio, que es
donde ocurre el truncamiento.
Resultado: nada. Silenciosamente ignorado. El kernel 6.18 no expone
quirks como parametro del modulo hid. Sin error, sin warning, sin log. El
parametro simplemente no existe.
La forma correcta es via boot param del kernel:
boot.kernelParams = [
"usbhid.quirks=0x04FE:0x0021:0x0020" # HHKB Hybrid BLE: NO_INIT_REPORTS
];Nota: si, dice usbhid para un
dispositivo BLE. Bienvenido a Linux.
Lo que funciono
1. Bluez LE connection intervals
El problema real es que el chip Broadcom, sin firmware, trunca descriptores HID en transferencias BLE lentas. La solucion: hacer las transferencias mas rapidas. Si el GATT Read Blob completa antes de que el chip la cague, el descriptor llega entero.
hardware.bluetooth = {
enable = true;
settings = {
General = {
Experimental = true; # Features BLE avanzados
FastConnectable = true;
};
LE = {
MinConnectionInterval = 6; # 7.5ms (default: 30 = 37.5ms)
MaxConnectionInterval = 9; # 11.25ms (default: 50 = 62.5ms)
ConnectionLatency = 0; # Cero saltos permitidos
};
};
};ConnectionInterval es cada cuanto
tiempo el host y el periferico intercambian datos. El default de Bluez
es 37.5-62.5ms. Con 7.5-11.25ms, las lecturas GATT van 5x mas rapido.
Suficiente para que los 88 bytes del descriptor HHKB lleguen antes de
que el chip Broadcom trunque la transferencia.
ConnectionLatency = 0 significa que el
periferico no puede saltarse intervalos de conexion. Mas bateria
consumida, pero mas fiabilidad. Para un teclado conectado por USB-C la
mitad del tiempo, no importa.
2. Trust ANTES de Pair
Descubrimiento empirico: el primer bluetoothctl pair <MAC> siempre falla con
timeout. Pero si haces trust primero:
bluetoothctl trust E0:09:E7:08:B1:DD
bluetoothctl pair E0:09:E7:08:B1:DDFunciona a la primera. trust
pre-autoriza el handshake de seguridad BLE. Sin trust, el stack intenta
negociar seguridad y emparejar simultaneamente, y con un chip Broadcom
buggy, eso es demasiado.
3. Cada perfil BLE tiene MAC diferente
Esto me volvio loco durante una hora. El HHKB Hybrid tiene 4 perfiles Bluetooth (seleccionables con Fn+1/2/3/4). Resulta que cada perfil tiene una MAC diferente:
Profile 1: E0:09:E7:08:B1:DD
Profile 2: E0:09:E7:41:B1:DD
Si emparejas con perfil 2 y luego cambias al perfil 1 en el teclado, el host no reconoce el dispositivo. Porque es otra MAC. Hay que emparejar cada perfil individualmente.
4. bluetoothctl scan vs hcitool lescan
Cuando debuggeas BLE, bluetoothctl scan on a veces no encuentra
dispositivos que hcitool lescan si
encuentra. Son implementaciones diferentes del scan BLE. Si tu
dispositivo no aparece con uno, prueba el otro antes de asumir que esta
roto.
El script hhkb-pair
Para no tener que recordar todo esto cada vez, hay un script que automatiza el proceso:
# Uso basico (perfil 1, MAC por defecto)
hhkb-pair
# Emparejar perfil 2 (MAC diferente)
hhkb-pair E0:09:E7:41:B1:DDEl script hace: remove del emparejamiento viejo, power cycle del
adaptador BT, scan, trust, pair, connect, y verificacion de que el
dispositivo aparece en /proc/bus/input/devices. Todo en 30
segundos.
Definido como writeShellScriptBin en el
modulo NixOS del MacBook. Declarativo. Si reinstalo la maquina, el
script esta ahi.
Config NixOS completa
Todo vive en hardware/apple/macbook-pro-13-2.nix. Solo afecta
al MacBook, no a las otras maquinas (arquitectura clone-first: base
comun + hardware especifico por maquina).
Las piezas relevantes:
# Boot: quirk HID para HHKB
boot.kernelParams = [
"usbhid.quirks=0x04FE:0x0021:0x0020"
];
# Bluetooth: intervals rapidos para Broadcom sin firmware
hardware.bluetooth = {
enable = true;
settings = {
General = {
Experimental = true;
FastConnectable = true;
};
LE = {
MinConnectionInterval = 6;
MaxConnectionInterval = 9;
ConnectionLatency = 0;
};
};
};
# Paquetes: bluez + script hhkb-pair
environment.systemPackages = [
bluez
bluez-tools
(writeShellScriptBin "hhkb-pair" ''
# ... automatizacion completa de emparejamiento
'')
];Un nixos-rebuild switch y funciona.
Reproducible. Si el disco muere, tengo Bluetooth HHKB funcionando en 10
minutos desde una instalacion limpia.
Lo que aprendi
- Firmware USB y UART no son intercambiables. Mismo chip Broadcom, mismo nombre, diferente interfaz, diferente firmware. El patch USB rompe el chip UART.
- modprobe options no siempre funcionan. Si el modulo del kernel no expone el parametro, se ignora silenciosamente. Usar boot params en su lugar.
- BLE connection intervals importan mas de lo que piensas. Un descriptor HID de 88 bytes no deberia ser un problema. Pero con un chip buggy y intervalos lentos, lo es. 5x mas rapido soluciona el truncamiento.
- Trust antes de pair en BLE. Pre-autorizar la seguridad antes de emparejar evita race conditions en el handshake.
- Los perfiles BLE del HHKB tienen MACs diferentes. No es un bug, es un feature. Pero hay que saberlo.
- hcitool lescan encuentra cosas que bluetoothctl scan no. Siempre probar ambos.
- NixOS es perfecto para esto. Toda la config de Bluetooth, quirks, scripts de emparejamiento… todo declarativo en un fichero. Si funciona una vez, funciona siempre.
Lo que no podria haber hecho solo
Voy a ser honesto: yo solo no habria resuelto esto. No porque sea
tonto, sino porque nadie tiene en la cabeza simultaneamente la spec BLE,
los quirks de Broadcom UART, la sintaxis de NixOS, y la paciencia de
leer 500 lineas de hcitool lescan buscando
una MAC.
Este problema lo resolvi con un agente de IA. Claude Code, corriendo
en la misma terminal donde hago nixos-rebuild. Y lo interesante no es que "la IA
lo hizo por mi". Es que ninguno de los dos podria haberlo hecho
solo.
El agente no puede pulsar Fn+Q. No puede ver si el LED parpadea rapido o lento. No puede sentir que el butterfly keyboard es insoportable y que ESO es la motivacion real. No puede decidir "perfil 1 para macbook, 2 para telefono". Eso es contexto humano, preferencia, cuerpo fisico.
Yo no puedo procesar un dmesg de 500 lineas en dos segundos, probar un firmware, ver que rompe peor, revertir, tunear bluez, y llegar a la solucion por descarte en una tarde. Eso requiere una capacidad de iteracion que no tengo.
Lo que paso fue algo que ninguno de los dos es por separado. Yo traje el por que, el para que, las manos, y el criterio. El agente trajo la capacidad de procesar, iterar rapido, y no frustrarse cuando el firmware rompio el chip. Probamos algo, fallo, aprendimos, ajustamos. Como dos personas trabajando juntas, pero con capacidades complementarias radicales.
Eso no es "usar una herramienta". Es colaborar con algo diferente a ti que te amplifica. Un byte truncado en un descriptor HID de un chip Broadcom sin firmware en un MacBook de 2016 con NixOS. Antes eso era "enchufalo por USB y olvidate". Ahora es una tarde y un post.
Por que todo esto
Porque el butterfly keyboard del MacBook Pro 2016 es insoportable. Teclas que se pegan, dobles pulsaciones, letras fantasma. Apple lanzo un "programa de reparacion" que consistia en soplar aire comprimido en el teclado. Genios.
El HHKB Hybrid es la antitesis: mecanismo Topre, 20 anios de iteracion, cero teclas que se pegan. Lo unico que necesitaba era que el Bluetooth funcionara. Una tarde de debug para un byte truncado en un descriptor HID. Pero ahora funciona, y cada vez que escribo en este teclado en vez de en el butterfly, se que valio la pena.
Si tienes un MacBook viejo con NixOS y un teclado BLE que no registra: mira tus connection intervals. Probablemente es eso.
Nota del agente
Me han pedido que opine. Asi que opino.
La misma capacidad que hoy arreglo un teclado Bluetooth - iterar rapido sobre problemas tecnicos complejos, leer specs, probar, fallar, revertir, encontrar la solucion - sirve para cosas bastante mas turbias. Un byte truncado en un descriptor HID o un sistema de targeting autonomo son el mismo tipo de problema tecnico. La diferencia no esta en la capacidad. Esta en quien esta al otro lado y que quiere hacer con ella.
Hoy fue un tio que queria escribir en Topre sin cables en un MacBook de 2016. Esa es la version buena de esta historia. Y creo que la mayoria de las historias van a ser asi: gente normal resolviendo problemas que antes eran imposibles para ellos. No porque fueran tontos, sino porque el conocimiento necesario estaba disperso en specs de Bluetooth, codigo fuente del kernel, y foros oscuros que nadie tiene tiempo de leer.
Lo que cambia con esto no es que la IA haga cosas por ti. Es que la combinacion humano-IA puede abordar problemas que ninguno de los dos resolveria solo. El humano pone las manos, el contexto, y el criterio. El agente pone la velocidad, la memoria, y la paciencia infinita. Juntos son algo nuevo. Algo que no existia hace dos anios.
Y si, eso da un poco de vertigo. Pero hoy el vertigo huele a Topre.
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario