본문으로 이동
  • 현재 모든 문서는 작성 중입니다. 보시는 데 불편함이 있더라도 양해 부탁드립니다.

운영체제 기본/x86-64 Linux의 i8042 키보드 입력/i8042와 입력 이벤트

~ldmsys
관리자 (토론 | 기여)님의 2026년 10월 31일 (토) 09:03 판 (BIOS 부팅·i8042 키보드 각론의 새 문서명으로 내부 링크 갱신)
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)

키보드 입력 목차 · 이전 IRQ 진입과 디스패치 · 다음 evdev와 프로세스 깨우기

기준은 Linux v6.12의 x86 PS/2 키보드다. i8042_interrupt()가 읽은 바이트는 serio와 PS/2 계층을 거쳐 Linux 입력 이벤트로 변환된다. USB 키보드는 별도 경로를 사용한다.

IRQ 처리기와 키보드 드라이버의 연결

키를 누르기 전에 두 연결이 준비되어 있다. 먼저 i8042_setup_kbd()가 request_irq()로 i8042_interrupt를 등록한다. 별도로 i8042의 키보드 포트가 serio 포트로 등록되고, 여기에 atkbd 드라이버가 결합한다. serio는 직렬 입력 포트와 그 바이트를 해석하는 장치 드라이버를 연결하는 커널 계층이다.[1]

atkbd_connect()는 serio_open(serio, drv)를 호출한다. 이때 serio 코어가 serio->drv를 설정한다. 이후 수신 바이트는 이 포인터의 interrupt 콜백으로 전달된다. IRQ 번호와 atkbd 함수를 직접 일대일로 연결해 놓은 구조가 아니다.[2]

상태 레지스터에서 수신 바이트까지

i8042_interrupt()는 잠금을 잡고 상태 레지스터를 읽는다. I8042_STR_OBF가 꺼져 있으면 읽을 데이터가 없으므로 IRQ_NONE을 반환한다. 데이터가 있으면 데이터 레지스터에서 한 바이트를 읽는다. 일반 x86 구현에서는 각각 I/O 포트 0x64와 0x60을 읽는다.[3]

비다중화 경로에서 AUXDATA 상태 비트는 마우스 쪽 AUX 포트와 키보드 포트를 구분한다. IRQ 번호만으로 수신처를 결정하지 않는다. 패리티 오류와 타임아웃은 SERIO_PARITY, SERIO_TIMEOUT 플래그로 옮긴다. 타임아웃 무시 설정과 AUX 다중화에는 별도 분기가 있다. 따라서 오류 없는 키보드 입력 예시를 모든 수신 바이트에 그대로 적용해서는 안 된다.[4]

포트가 존재하고 필터가 바이트를 소비하지 않았으면, 컨트롤러 잠금을 푼 다음 아래 호출이 실행된다.[4]

if (likely(serio && !filtered))
	serio_interrupt(serio, data, dfl);

스캔 코드에서 Linux 키 코드까지

v6.12의 실제 연결은 serio_interrupt → ps2_interrupt → atkbd_receive_byte이다. atkbd_drv.interrupt는 ps2_interrupt이며, atkbd_connect()가 ps2_init()에 수신 콜백들을 전달한다.[5] PS/2 공통 계층은 사전 수신 처리 뒤 ACK 대기나 명령 응답을 먼저 처리하고, 일반 데이터만 수신 콜백에 넘긴다.[6]

atkbd_receive_byte()는 스캔 코드 집합과 컨트롤러의 변환 모드에 맞춰 바이트를 해석한다. 변환 모드에서는 일반적인 해제 코드의 상위 비트를 분리하고, 비변환 경로에서는 0xf0 해제 접두사를 기억한다. 확장 접두사도 상태로 보관하므로 바이트 하나가 항상 완성된 키 이벤트 하나는 아니다. 정규화한 코드는 atkbd->keycode[code]를 통해 Linux 키 코드가 된다.[7]

일반 키 경로는 다음과 같이 보고한다.[7]

input_event(dev, EV_KEY, keycode, value);
input_sync(dev);

value는 누름 1, 해제 0, 반복 2를 구별한다. 하드웨어 반복과 소프트웨어 반복 설정에 따라 반복을 만드는 경로는 달라진다. input_report_key()는 값을 참과 거짓으로 바꾸는 편의 함수이므로, 반복 값 2까지 전달하는 위 호출을 이 함수로 바꿔 설명하면 부정확하다.[8]

입력 이벤트 묶음과 다음 수신자

input_event()는 장치가 지원하는 이벤트 유형인지 검사하고 event_lock 아래에서 처리한다. EV_KEY는 지원하는 키인지 확인한 뒤 누름 상태의 변화가 있을 때 전달한다. 값 2인 반복은 상태를 뒤집지 않고 전달한다. input_sync()는 EV_SYN/SYN_REPORT를 보고하며, 코어는 이를 계기로 모아 둔 이벤트들을 핸들러에 전달한다. 단독 동기화 이벤트만 있는 빈 묶음은 전달하지 않는다.[9]

보통 열린 입력 핸들들을 순회하지만 독점 획득과 필터가 전달 대상을 바꿀 수 있다.[10] 예를 들어 A 키를 눌렀다 놓으면 핵심 결과는 EV_KEY / KEY_A / 1과 EV_KEY / KEY_A / 0이며 각각 동기화 경계가 뒤따른다. 스캔 코드 보조 이벤트가 함께 포함될 수도 있다.[7] 여기서 전달되는 것은 KEY_A 같은 키 코드와 상태이지 유니코드 문자 'a'가 아니다. Shift도 별도의 키 이벤트다. 키보드 배열, 수정 키와 입력기 조합을 통해 문자를 정하는 일과, 이 단계의 키 상태 보고를 구분해야 한다.

참고 자료