Teclado
A interface de teclado PS/2, embora fisicamente obsoleta na maior parte do hardware atual, permanece disponível em praticamente toda máquina x86 através de emulação legada de USB em nível de firmware, tornando-a o caminho mais simples de entrada de teclado que um kernel hobby consegue implementar. Um teclado conectado dessa forma se comunica através do controlador de teclado 8042, um chip (ou, em hardware moderno, um equivalente emulado por firmware) sentado entre o teclado físico e duas portas fixas de I/O.
O controlador 8042
Seção intitulada “O controlador 8042”Duas portas expõem o controlador: 0x60, a porta de dados, usada para ler um byte que o teclado enviou ou escrever um byte para o teclado ou para o próprio controlador; e 0x64, que se comporta de forma diferente dependendo da direção: lê-la retorna um registrador de status, enquanto escrever nela envia um comando ao controlador, não ao teclado. O bit baixo do registrador de status, Output Buffer Full, indica se um byte de fato está esperando para ser lido da porta de dados; software fazendo polling do controlador verifica esse bit antes de ler 0x60, e um driver orientado a interrupção depende da IRQ1 disparar apenas quando um byte de fato chegou, sem precisar fazer polling algum.
O próprio controlador, distinto do teclado conectado a ele, aceita seus próprios comandos pela porta 0x64: lendo ou escrevendo seu byte de configuração, habilitando ou desabilitando qualquer uma de suas duas portas (um 8042 legado também multiplexa uma segunda porta PS/2, convencionalmente usada para um mouse), e rodando um autoteste do controlador. Um kernel inicializando entrada PS/2 geralmente desabilita ambas as portas primeiro, descarta qualquer byte desatualizado sentado no buffer de saída, configura o controlador (em particular, garantindo que tradução e interrupções estejam definidas como pretendido), e só então reabilita a porta que pretende usar.
Scancodes
Seção intitulada “Scancodes”Um teclado não envia dados de caractere: ele envia scancodes, identificadores numéricos para posições físicas de tecla, com a tradução de scancode para caractere acontecendo inteiramente em software. Toda tecla produz um make code quando pressionada e um break code correspondente quando solta, permitindo que o software distinga uma tecla sendo mantida pressionada de um único toque, e rastreie corretamente o estado de teclas modificadoras como Shift ou Ctrl ao longo de uma sequência de outras teclas pressionadas.
Vários scancode sets existem por razões históricas, diferindo exatamente em como make e break codes são codificados. O Set 1, a codificação original do IBM PC XT, representa um break code como o make code com o bit alto definido: a tecla 0x1E pressionada e solta produz 0x1E depois 0x9E. O Set 2, o padrão que o próprio teclado de fato gera hoje, em vez disso prefixa um break code com o byte 0xF0, em vez de definir um bit alto, e prefixa os make codes de várias teclas com 0xE0 para marcá-las como “estendidas” (uma distinção necessária porque os códigos compactos de um único byte do Set 2 foram originalmente atribuídos ao conjunto menor de teclas de um teclado antigo, sem deixar espaço para teclas adicionadas depois, exceto tomando emprestado um byte como prefixo de escape). O controlador 8042, quando configurado para tradução (o padrão comum), converte silenciosamente o que quer que o teclado de fato envie em Set 2 de volta para Set 1 antes que o byte sequer chegue à porta de dados, razão pela qual a maioria dos kernels, e este artigo, descreve o Set 1, apesar de o próprio hardware de teclado falar Set 2 por baixo.
Lendo a entrada
Seção intitulada “Lendo a entrada”Um driver orientado a interrupção registra um handler na IRQ1 (remapeada para qualquer vetor que a configuração de PIC ou APIC do kernel atribua a ela) e lê o byte pendente da porta 0x60 toda vez que a interrupção dispara:
void keyboard_irq_handler(void) { uint8_t scancode = inb(0x60); // despacha para o caminho de scancode-para-caractere abaixo pic_send_eoi(1);}Scancodes estendidos exigem manter uma pequena quantidade de estado entre interrupções, em vez de processar todo byte isoladamente: um byte 0xE0 sozinho não carrega informação de tecla alguma: ele sinaliza que o próximo byte pertence a uma tecla estendida, e o driver precisa lembrar disso e combinar os dois bytes antes de consultar a tecla real.
De scancodes a caracteres
Seção intitulada “De scancodes a caracteres”Converter um scancode em um caractere utilizável é uma consulta a tabela, não um cálculo: um array fixo indexado por scancode mapeia cada tecla ao caractere que produz no caso mais simples, com uma tabela separada, ou uma transformação em tempo de execução da mesma tabela, aplicada quando Shift está pressionado. As próprias teclas modificadoras (Shift, Ctrl, Alt) não são consultadas nessa tabela de forma alguma; em vez disso, um driver rastreia seu estado pressionado/solto diretamente a partir de seus próprios make e break codes, já que o papel de um modificador é mudar como outras teclas são interpretadas, não produzir um caractere próprio. Um driver mínimo só precisa rastrear esse estado corretamente, aplicar a tabela certa com base nele, e tratar o pequeno número de teclas (Caps Lock à frente delas) cujo efeito persiste ao longo de teclas pressionadas, em vez de durar apenas enquanto fisicamente mantida.
O layout de teclado é uma preocupação inteiramente separada do tratamento de scancode: um scancode identifica uma posição física de tecla, não um caractere impresso, então o mesmo scancode para a tecla entre Tab e Caps Lock produz Q sob um layout QWERTY e A sob um AZERTY puramente por causa de qual tabela de consulta é usada, com a própria lógica de leitura de scancode não afetada pela escolha de layout.
Emulação legada USB
Seção intitulada “Emulação legada USB”Praticamente todo teclado vendido hoje é USB, não PS/2, e ainda assim o acesso estilo PS/2 continua funcionando porque o firmware da plataforma implementa emulação legada de USB: o próprio firmware do sistema intercepta os relatórios HID do teclado USB real e os re-apresenta ao software como se estivessem chegando de um controlador 8042 nas portas convencionais, inteiramente de forma transparente para um sistema operacional que ainda não inicializou sua própria pilha USB. Isso é especificamente o que torna um driver estilo PS/2 um ponto de partida razoável para um kernel hobby: funciona em praticamente qualquer máquina x86 real e sob qualquer emulador comum, sem exigir que suporte USB exista primeiro, mesmo que a própria porta física PS/2 tenha em grande parte desaparecido do hardware atual.
Notas de implementação
Seção intitulada “Notas de implementação”Um driver orientado a interrupção que falha em ler a porta de dados antes de retornar de seu handler deixa o byte sentado no buffer de saída do controlador, o que na maioria das implementações bloqueia a IRQ1 de disparar novamente até que seja drenado: um teclado que parece parar de responder por completo, depois de funcionar corretamente na primeiríssima tecla pressionada, é um sintoma comum exatamente disso. PS/2 como protocolo também não tem conceito algum de reportar de forma confiável mais do que um pequeno número fixo de teclas simultaneamente pressionadas, uma limitação geralmente chamada de N-key rollover, ou sua ausência, o que raramente importa para digitação mas vale a pena saber antes de depender de detectar muitas teclas mantidas pressionadas ao mesmo tempo, um caso onde teclados USB HID tipicamente se saem melhor do que a emulação PS/2 colocada sobre eles.
Referências
Seção intitulada “Referências”- ^ OSDev Wiki, “PS/2 Keyboard”: uma referência hobbyist detalhada cobrindo diferenças de scancode set e sequências de comando de controlador com mais profundidade do que cabe aqui.
- ^ IBM, Personal System/2 Hardware Interface Technical Reference: a especificação original da interface de comando do controlador 8042.
Ver também
Seção intitulada “Ver também”- PIC & APIC: como a IRQ1 é roteada até a CPU antes do handler deste driver rodar.
- Serial Port: um caminho de I/O mais simples e frequentemente mais confiável para depuração inicial.
- Mouse PS/2: o segundo canal do mesmo controlador 8042, com seu próprio protocolo de pacote.