OSDev Wiki
Регистрация
Advertisement

Ранние клавиатуры были однонаправленными устройствами, однако уже в IBM PC/AT стали применяться клавиатуры, которые не только передавали скан-коды компьютеру, но и могли получать от него команды. Все современные клавиатуры с точки зрения программирования совместимы с клавиатурами IBM PC/AT.

Современные клавиатуры теоретически предоставляют возможность выбора из трех таблиц скан-кодов, однако обязательно присутствует лишь таблица №2, используемая по умолчанию; именно она использовалась в IBM PC/AT.

Помимо скан-кодов, от клавиатуры могут быть получены следующие коды:

  • 0x00: ошибка клавиатуры (нажаты одновременно слишком много клавиш);
  • 0xAA: BAT (Basic Assurance Test, встроенный тест) завершён;
  • 0xEE: эхо (ECHO);
  • 0xFA: ответ (ACK);
  • 0xFE: необходима повторная отправка данных в клавиатуру (RESEND);
  • 0xFF: ошибка клавиатуры.

Команды[]

Запись команд и их дополнительных байтов следует в порт 60h (опробовано на VMWare 9.0, XT), принимать ответы тоже из этого порта.

Если клавиатуре посылается что-либо недопустимая, она возвращает ответ RESEND (0xFE).

Любая команда очищает буфер приема клавиатуры.

Команды:

  • 0xED: Установить значение клавиатурных индикаторов (LEDs). В дополнительном байте передаются [2.3.3] Единичное значение бита показывает, что светодиод светится.
  • 0xEE: Эхо. На эту команду, используемую для диагностики, клавиатура отзывается кодом ECHO (0xEE).
  • 0xF0: Выбор таблицы скан-кодов. На саму команду клавиатура отвечает кодом ACK (0xFA). После этого ей передается номер таблицы (1, 2, 3), на что клавиатура снова отвечает кодом ACK. Если в качестве кода передается 0, после ACK клавиатура присылает номер текущей используемой таблицы скан-кодов.
  • 0xF2: Идентификация клавиатуры. Ранние клавиатуры, не умеющие принимать команды от ПК, не ответят ничего, и программа сможет обнаружить таймаут. Клавиатура PC/AT вернёт код ACK. Клавиатура MF II вернёт последовательность кодов ACK, 0xAB, 0x41.
  • 0xF3: Настройка скорости повтора символов. Дополнительный байт определяет скорость повтора. Младшие 5 разрядов задают саму скорость повтора, биты 6:5 — задержку до начала повтора. Старший бит должен быть равен нулю.
  • 0xF4: Разрешить работу клавиатуры. Эта команда очищает буфер и разрешает сканирование нажатых клавиш.
  • 0xF5: Сброс клавиатуры. Сканирование запрещено.
  • 0xF6: Сброс клавиатуры. Сканирование разрешено.
  • 0xFE: Повторная отправка последней передачи.
  • 0xFF: Сброс. Вызывает BAT, в случае успеха возвращает ACK. Выполнение этой команды может занять до 750ms.

Таблицы[]

Скорость повтора[]

Значение Скорость (cps)
0x00 30.0
0x01 26.7
0x02 24.0
0x03 21.8
0x04 20.7
0x05 18.5
0x06 17.1
0x07 16.0
0x08 15.0
0x09 13.3
0x0A 12.0
0x0B 10.9
0x0C 10.0
0x0D 9.2
0x0E 8.6
0x0F 8.0
0x10 7.5
0x11 6.7
0x12 6.0
0x13 5.5
0x14 5.0
0x15 4.6
0x16 4.3
0x17 4.0
0x18 3.7
0x19 3.3
0x1A 3.0
0x1B 2.7
0x1C 2.5
0x1D 2.3
0x1E 2.1
0x1F 2.0

Задержка[]

Значение Задержка(секунд) 0x00 0.25 0x01 0.50 0x02 0.75 0x03 1.00


Индикаторы[]

бит 0 ScrollLock бит 1 NumLock бит 2 CapsLock


Инициализация[]

Пример сценария инициализации:

  1. Host: ED Set/Reset Status Indicators
  2. Keyboard: FA Acknowledge
  3. Host: 00 Turn off all LEDs
  4. Keyboard: FA Acknowledge
  5. Host: F2 Read ID
  6. Keyboard: FA Acknowledge
  7. Keyboard: AB First byte of ID
  8. Host: ED Set/Reset Status Indicators ;BIOS init
  9. Keyboard: FA Acknowledge
  10. Host: 02 Turn on Num Lock LED
  11. Keyboard: FA Acknowledge
  12. Host: F3 Set Typematic Rate/Delay ;Windows init
  13. Keyboard: FA Acknowledge
  14. Host: 20 500 ms / 30.0 reports/sec
  15. Keyboard: FA Acknowledge
  16. Host: F4 Enable
  17. Keyboard: FA Acknowledge
  18. Host: F3 Set Typematic Rate/delay
  19. Keyboard: FA Acknowledge
  20. Host: 00 250 ms / 30.0 reports/sec
  21. Keyboard: FA Acknowledge

Непонятое[]

Порт 0x61

Исторически (на IBM PC) порты 0x60-0x63 принадлежали параллельному контроллеру типа 8255. Первые три порта использовались для управления различными устройствами, в частности, клавиатурой и динамиком, а последний (0x63) применялся для программирования самого контроллера 8255. Позднее этот контроллер был упразднён, а его функции частично перешли к специализированной микросхеме: в основном сохранились функции портов 0x60 и 0x61.

В XT порт 0x61 служит для оповещения контроллера о приёме очередного скан-кода. Биты с нулевого по пятый используются для всякой ерунды типа динамика, шлюза A20 и т.д... Эти биты нельзя сбрасывать, их надо бережно сохранять. А вот старшие два бита для нас представляют интерес.

Для того, чтобы уведомить клавиатуру о том, что скан-код нами принят, нужно установить и тут же снова сбросить 7-й бит. Все остальные биты сохраняются.

Что касается шестого бита, - его установка повлечёт за собой сохранение клавиатурных часов в низком и недоступном для детей месте :) (Hold keyboard clock low -> Keyboard can't send any data).

В AT контроллер 8042 этот порт мало интересен. И имеет другое назначение. Зачастую он предназначен только для чтения. И информирует о всякой всячине. Более того не принадлежит контроллеру клавиатуры, а эмулируется системной логикой. Это делается для совместимости.

Что касается оповещения о приеме скан кода, то это делается путем чтения порта 0x60 или через сброс флага буфера приема.


Примеры[]

Инициализация клавиатуры.[]

Взято из [6.1]

// Запретить все
i8042_write_command_byte (I8042_CMD_DISABLE_ALL);

// Очистить буфер.
for (i = 0; i < MAX_JUNK_ITERATIONS; i++)
{
    stat = ddi_get8 (I8042_STAT);
    if (! (stat & I8042_STAT_OUTBF))
        break;
    ddi_get8 (I8042_DATA);
}

// Зарегистрировать прерыывание

// Разрешить все.
i8042_write_command_byte (I8042_CMD_ENABLE_ALL);

Интро[]

i8042_write_command_byte (cb)
{
    i8042_send(I8042_CMD, I8042_CMD_WCB);
    i8042_send(I8042_DATA, cb);
}

i8042_send (cb)
{
    // Эта операция производится в цикле
    // Максимальное время ожидания - 250ms
    stat = ddi_get8(I8042_STAT);
    if ((stat & I8042_STAT_INBF) == 0)
        ddi_put8(reg, val);
}

Обработчик прерывания[]

// Читаем статус
stat = ddi_get8(I8042_STAT);

if (! (stat & I8042_STAT_OUTBF))
{
	// Левое прерывание
	return (DDI_INTR_UNCLAIMED);
}

// Читаем данные
byte = ddi_get8(I8042_DATA);

// Смотрим к кому относятся (Во как интересно!)
which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;

См. также[]

Advertisement