Pular para o conteúdo

Controlador de Disquete (FDC)

O disquete não é mais hardware relevante na prática, mas seu controlador continua sendo um dos primeiros drivers de armazenamento que muitos kernels hobbyist escrevem especificamente por ser simples o suficiente para implementar sem DMA moderno nem uma pilha de comandos complexa: diferente de ATA PIO e AHCI, os dois drivers de armazenamento que de fato vale a pena visar em hardware real hoje, o FDC existe nesta wiki puramente por seu valor didático.

O controlador de disquete primário fica numa porta de I/O base fixa, 0x3F0, com um punhado de registradores alcançados em offsets fixos a partir dela: o Main Status Register (0x3F4) reporta se o controlador está pronto para um byte de comando ou um byte de resultado, e se está ocupado no momento; o Data Register (0x3F5) é onde bytes de comando são escritos e bytes de status/resultado são lidos de volta, ambos através da mesma porta.

#define FDC_MSR 0x3F4 // Main Status Register (leitura)
#define FDC_DATA 0x3F5 // Data Register (leitura/escrita)
#define FDC_DOR 0x3F2 // Digital Output Register (escrita)
#define MSR_RQM 0x80 // Request For Master: FDC pronto para transferência de dados
#define MSR_DIO 0x40 // Direção de I/O de dados: 1 = FDC->CPU, 0 = CPU->FDC

O Digital Output Register (0x3F2) controla o próprio drive físico, separado da interface de comando do próprio controlador: um bit para ligar ou desligar o motor do drive selecionado, um bit para selecionar qual dos até quatro drives os comandos subsequentes visam, e, criticamente, um bit de reset que precisa ser pulsado para tirar o controlador do estado de reset antes de qualquer comando ser aceito.

Todo comando de FDC segue o mesmo padrão: sondar MSR até RQM estar definido e DIO ler 0 (pronto para aceitar um byte de comando), escrever o comando e seus bytes de parâmetro um de cada vez no registrador de dados, depois, para comandos que produzem um resultado, sondar MSR de novo por RQM definido com DIO lendo 1 e ler de volta cada byte de resultado da mesma forma.

void fdc_send_byte(uint8_t byte) {
for (int timeout = 0; timeout < 1000; timeout++) {
if ((inb(FDC_MSR) & 0xC0) == 0x80) { // RQM=1, DIO=0
outb(FDC_DATA, byte);
return;
}
}
}
uint8_t fdc_read_byte(void) {
for (int timeout = 0; timeout < 1000; timeout++) {
if ((inb(FDC_MSR) & 0xC0) == 0xC0) { // RQM=1, DIO=1
return inb(FDC_DATA);
}
}
return 0xFF;
}

O comando Sense Interrupt Status precisa ser emitido depois de todo comando que gera uma interrupção, incluindo o próprio reset, antes de o controlador aceitar um comando adicional; pular isso é uma fonte comum de um controlador que parece travar depois de sua primeiríssima operação bem-sucedida, já que o FDC está especificamente esperando por esse reconhecimento em vez de qualquer comando novo.

Uma leitura de disquete move dados através do controlador de DMA ISA legado do sistema, canal 2, inteiramente separado do motor de DMA que um dispositivo PCIe moderno como o AHCI usa para suas próprias transferências: o driver programa o canal 2 com o endereço físico do buffer de destino e o comprimento da transferência antes de emitir o próprio comando Read Data do FDC, e os dois controladores, DMA e FDC, coordenam a transferência real entre si uma vez que os dois estão configurados.

void fdc_setup_dma_read(uint32_t phys_addr, uint16_t length) {
outb(0x0A, 0x06); // mascara o canal 2 de DMA
outb(0x0C, 0xFF); // limpa o flip-flop
outb(0x04, phys_addr & 0xFF);
outb(0x04, (phys_addr >> 8) & 0xFF);
outb(0x81, (phys_addr >> 16) & 0xFF); // registrador de página do canal 2
outb(0x05, (length - 1) & 0xFF);
outb(0x05, ((length - 1) >> 8) & 0xFF);
outb(0x0B, 0x46); // transferência única, modo leitura, canal 2
outb(0x0A, 0x02); // desmascara o canal 2
}

Setores são lidos um de cada vez, especificados por número de cilindro, cabeça, e setor (endereçamento CHS, o mesmo esquema legado que Tabelas de Partição já cobre para o próprio MBR) em vez de um LBA plano da forma que drives modernos endereçam armazenamento, já que o disquete é anterior à própria existência do endereçamento LBA como padrão.

Uma imagem de disquete não exige tabela de partição alguma, nenhum sistema de arquivos além de no máximo um volume FAT12 simples, e enumeração PCI alguma, já que o controlador fica num endereço de I/O legado fixo e não descobrível em vez de precisar ser localizado; o primeiríssimo driver de armazenamento de um kernel pode ser testado contra uma imagem de disquete bruta sob o QEMU (-fda disk.img) com essencialmente nenhuma configuração além dos próprios comandos do controlador. Isso é especificamente o que o torna um veículo de ensino razoável apesar de sua completa falta de relevância prática: o handshake de comando/status, a sequência de reset, e a configuração de DMA legado todos se transferem conceitualmente para hardware mais relevante, sem a complexidade adicional que descoberta PCI e DMA moderno adicionam por cima.

O canal de DMA ISA legado de 64 KB não consegue endereçar uma transferência que cruza uma fronteira física de 64 KB, e diferente de DMA scatter-gather moderno, não tem forma alguma de dividir uma transferência automaticamente quando isso acontece: um buffer alocado sem checar isso cruza a fronteira silenciosamente e corrompe parte da transferência, motivo pelo qual buffers de DMA de disquete são convencionalmente alocados de um pool garantido para não cruzar essa fronteira em vez de um endereço físico arbitrário. O motor do FDC tem um atraso real de partida (comumente algumas centenas de milissegundos) antes de o drive de fato estar pronto para ler, e um comando emitido antes desse atraso decorrer falha ou retorna lixo em vez de esperar automaticamente, então um driver precisa rastrear o estado do motor ele mesmo e inserir um atraso explícito depois de ligá-lo em vez de presumir que o hardware trata esse timing sozinho.

  1. ^ Intel, 8272A/8272 Floppy Disk Controller Datasheet: a referência original de comandos e registradores com a qual o 82077AA e controladores posteriores permanecem compatíveis.
  • ATA/IDE em Modo PIO: um driver de armazenamento mais relevante na prática, compartilhando a abordagem baseada em polling e em nível de registrador deste artigo.
  • AHCI: a interface de armazenamento de fato relevante em hardware moderno.