요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
file: uapi/v4l/keytable.c
=========================
.. code-block:: c
/* keytable.c - This program allows checking/replacing keys at IR
Copyright (C) 2006-2009 Mauro Carvalho Chehab <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#include "parse.h"
void prtcode (int *codes)
{
struct parse_key *p;
for (p=keynames;p->name!=NULL;p++) {
if (p->value == (unsigned)codes[1]) {
printf("scancode 0x%04x = %s (0x%02x)\\n", codes[0], p->name, codes[1]);
return;
}
}
if (isprint (codes[1]))
printf("scancode %d = '%c' (0x%02x)\\n", codes[0], codes[1], codes[1]);
else
printf("scancode %d = 0x%02x\\n", codes[0], codes[1]);
}
int parse_code(char *string)
{
struct parse_key *p;
for (p=keynames;p->name!=NULL;p++) {
if (!strcasecmp(p->name, string)) {
return p->value;
}
}
return -1;
}
int main (int argc, char *argv[])
{
int fd;
unsigned int i, j;
int codes[2];
if (argc<2 || argc>4) {
printf ("usage: %s <device> to get table; or\\n"
" %s <device> <scancode> <keycode>\\n"
" %s <device> <keycode_file>n",*argv,*argv,*argv);
return -1;
}
if ((fd = open(argv[1], O_RDONLY)) < 0) {
perror("Couldn't open input device");
return(-1);
}
if (argc==4) {
int value;
value=parse_code(argv[3]);
if (value==-1) {
value = strtol(argv[3], NULL, 0);
if (errno)
perror("value");
}
codes [0] = (unsigned) strtol(argv[2], NULL, 0);
codes [1] = (unsigned) value;
if(ioctl(fd, EVIOCSKEYCODE, codes))
perror ("EVIOCSKEYCODE");
if(ioctl(fd, EVIOCGKEYCODE, codes)==0)
prtcode(codes);
return 0;
}
if (argc==3) {
FILE *fin;
int value;
char *scancode, *keycode, s[2048];
fin=fopen(argv[2],"r");
if (fin==NULL) {
perror ("opening keycode file");
return -1;
}
/* Clears old table */
for (j = 0; j < 256; j++) {
for (i = 0; i < 256; i++) {
codes[0] = (j << 8) | i;
codes[1] = KEY_RESERVED;
ioctl(fd, EVIOCSKEYCODE, codes);
}
}
while (fgets(s,sizeof(s),fin)) {
scancode=strtok(s,"\\n\\t =:");
if (!scancode) {
perror ("parsing input file scancode");
return -1;
}
if (!strcasecmp(scancode, "scancode")) {
scancode = strtok(NULL,"\\n\\t =:");
if (!scancode) {
perror ("parsing input file scancode");
return -1;
}
}
keycode=strtok(NULL,"\\n\\t =:(");
if (!keycode) {
perror ("parsing input file keycode");
return -1;
}
// printf ("parsing %s=%s:", scancode, keycode);
value=parse_code(keycode);
// printf ("\\tvalue=%d\\n",value);
if (value==-1) {
value = strtol(keycode, NULL, 0);
if (errno)
perror("value");
}
codes [0] = (unsigned) strtol(scancode, NULL, 0);
codes [1] = (unsigned) value;
// printf("\\t%04x=%04x\\n",codes[0], codes[1]);
if(ioctl(fd, EVIOCSKEYCODE, codes)) {
fprintf(stderr, "Setting scancode 0x%04x with 0x%04x via ",codes[0], codes[1]);
perror ("EVIOCSKEYCODE");
}
if(ioctl(fd, EVIOCGKEYCODE, codes)==0)
prtcode(codes);
}
return 0;
}
/* Get scancode table */
for (j = 0; j < 256; j++) {
for (i = 0; i < 256; i++) {
codes[0] = (j << 8) | i;
if (!ioctl(fd, EVIOCGKEYCODE, codes) && codes[1] != KEY_RESERVED)
prtcode(codes);
}
}
return 0;
}
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
파일 목적과 사용 허가
1-20이 문서는 `uapi/v4l/keytable.c` 예제 프로그램의 전체 소스를 싣습니다. 프로그램은 IR 입력 장치의 scan code와 key code 대응표를 조회하거나 교체합니다.
저작권은 Mauro Carvalho Chehab에게 있으며 GPL 버전 2 조건으로 재배포하거나 수정할 수 있습니다. 유용성을 기대해 제공하지만 상품성이나 특정 목적 적합성에 대한 보증은 없습니다.
명령행 인자 수에 따라 같은 프로그램이 세 가지 작업을 수행합니다.
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
file: uapi/v4l/keytable.c
=========================
.. code-block:: c
/* keytable.c - This program allows checking/replacing keys at IR
Copyright (C) 2006-2009 Mauro Carvalho Chehab <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
헤더, 출력 함수와 key code 해석
21-60프로그램은 문자 분류, 오류, 파일 입출력, 문자열 처리, Linux input event 정의와 `ioctl()` 선언을 포함합니다. `parse.h`는 이름과 값의 대응인 `keynames` 및 `struct parse_key`를 제공합니다.
`prtcode()`는 `codes[1]`과 같은 값을 가진 이름을 `keynames`에서 찾습니다. 찾으면 scan code, symbolic key name과 16진 key code를 출력하고, 찾지 못하면 인쇄 가능한 문자는 문자와 16진 값을 함께, 그 밖의 값은 16진수만 출력합니다.
`parse_code()`는 대소문자를 구분하지 않고 주어진 문자열과 key name을 비교해 값을 반환합니다. 알려지지 않은 이름이면 `-1`을 반환하여 호출자가 숫자 문자열로 다시 해석하게 합니다.
Symbolic name을 우선하고 필요할 때 숫자로 전환합니다.
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#include "parse.h"
void prtcode (int *codes)
{
struct parse_key *p;
for (p=keynames;p->name!=NULL;p++) {
if (p->value == (unsigned)codes[1]) {
printf("scancode 0x%04x = %s (0x%02x)\\n", codes[0], p->name, codes[1]);
return;
}
}
if (isprint (codes[1]))
printf("scancode %d = '%c' (0x%02x)\\n", codes[0], codes[1], codes[1]);
else
printf("scancode %d = 0x%02x\\n", codes[0], codes[1]);
}
int parse_code(char *string)
{
struct parse_key *p;
for (p=keynames;p->name!=NULL;p++) {
if (!strcasecmp(p->name, string)) {
return p->value;
}
}
return -1;
}
명령행 검사와 단일 대응 설정
61-100`main()`은 device 인자에 더해 선택적으로 scan code와 key code 또는 key code 파일을 받습니다. 전체 인자 수가 2~4가 아니면 세 가지 사용 형식을 출력하고 실패로 끝냅니다. 원문의 세 번째 usage 문자열에 있는 `n`도 예제 그대로 보존됩니다.
입력 장치는 `O_RDONLY`로 열며 실패하면 `perror()`로 이유를 출력합니다. 인자가 네 개이면 세 번째 사용자 인자를 symbolic key name으로 먼저 해석하고, 이름이 없으면 `strtol()`로 숫자 값을 읽습니다.
`codes[0]`에는 scan code, `codes[1]`에는 key code를 넣습니다. `EVIOCSKEYCODE`로 대응을 설정하고 `EVIOCGKEYCODE`로 같은 scan code를 다시 조회해 성공 시 `prtcode()`로 실제 값을 보여 줍니다.
두 정수 배열은 scan code와 key code를 전달합니다.
int main (int argc, char *argv[])
{
int fd;
unsigned int i, j;
int codes[2];
if (argc<2 || argc>4) {
printf ("usage: %s <device> to get table; or\\n"
" %s <device> <scancode> <keycode>\\n"
" %s <device> <keycode_file>n",*argv,*argv,*argv);
return -1;
}
if ((fd = open(argv[1], O_RDONLY)) < 0) {
perror("Couldn't open input device");
return(-1);
}
if (argc==4) {
int value;
value=parse_code(argv[3]);
if (value==-1) {
value = strtol(argv[3], NULL, 0);
if (errno)
perror("value");
}
codes [0] = (unsigned) strtol(argv[2], NULL, 0);
codes [1] = (unsigned) value;
if(ioctl(fd, EVIOCSKEYCODE, codes))
perror ("EVIOCSKEYCODE");
if(ioctl(fd, EVIOCGKEYCODE, codes)==0)
prtcode(codes);
return 0;
}
파일에서 전체 표 교체
101-165인자가 세 개이면 두 번째 인자를 key code 파일로 열고, 열 수 없으면 오류를 보고합니다. 새 표를 적용하기 전에 0x0000부터 0xffff까지 모든 16-bit scan code에 `KEY_RESERVED`를 설정해 예전 표를 지웁니다.
각 입력 줄은 줄바꿈, 탭, 공백, 등호와 콜론을 구분자로 나눕니다. 첫 token이 `scancode`이면 다음 token을 실제 scan code로 사용하고, 이어지는 key code는 여는 괄호도 구분자에 포함해 읽습니다. 필요한 token이 없으면 즉시 parsing 오류로 종료합니다.
Key code는 `parse_code()`로 symbolic name을 먼저 찾고, 찾지 못하면 숫자로 해석합니다. Scan code도 `strtol()`로 읽어 `codes` 배열을 만들고 `EVIOCSKEYCODE`를 호출합니다. 설정 오류에는 두 값을 포함한 문맥을 출력하며, 성공 여부와 관계없이 조회가 가능하면 현재 대응을 출력합니다.
기존 표를 완전히 교체한 뒤 각 줄을 검증하며 반영합니다.
if (argc==3) {
FILE *fin;
int value;
char *scancode, *keycode, s[2048];
fin=fopen(argv[2],"r");
if (fin==NULL) {
perror ("opening keycode file");
return -1;
}
/* Clears old table */
for (j = 0; j < 256; j++) {
for (i = 0; i < 256; i++) {
codes[0] = (j << 8) | i;
codes[1] = KEY_RESERVED;
ioctl(fd, EVIOCSKEYCODE, codes);
}
}
while (fgets(s,sizeof(s),fin)) {
scancode=strtok(s,"\\n\\t =:");
if (!scancode) {
perror ("parsing input file scancode");
return -1;
}
if (!strcasecmp(scancode, "scancode")) {
scancode = strtok(NULL,"\\n\\t =:");
if (!scancode) {
perror ("parsing input file scancode");
return -1;
}
}
keycode=strtok(NULL,"\\n\\t =:(");
if (!keycode) {
perror ("parsing input file keycode");
return -1;
}
// printf ("parsing %s=%s:", scancode, keycode);
value=parse_code(keycode);
// printf ("\\tvalue=%d\\n",value);
if (value==-1) {
value = strtol(keycode, NULL, 0);
if (errno)
perror("value");
}
codes [0] = (unsigned) strtol(scancode, NULL, 0);
codes [1] = (unsigned) value;
// printf("\\t%04x=%04x\\n",codes[0], codes[1]);
if(ioctl(fd, EVIOCSKEYCODE, codes)) {
fprintf(stderr, "Setting scancode 0x%04x with 0x%04x via ",codes[0], codes[1]);
perror ("EVIOCSKEYCODE");
}
if(ioctl(fd, EVIOCGKEYCODE, codes)==0)
prtcode(codes);
}
return 0;
}
현재 scan code 표 조회
166-176추가 인자가 없으면 프로그램은 모든 16-bit scan code를 순회하며 `EVIOCGKEYCODE`로 현재 대응을 조회합니다. 조회에 성공하고 key code가 `KEY_RESERVED`가 아닌 항목만 출력합니다.
따라서 출력은 실제로 배정된 대응만 포함합니다. 모든 scan code를 순회하므로 이 예제는 단순하고 명확하지만 장치가 더 효율적인 열거 방식을 제공하더라도 이를 사용하지 않습니다.
/* Get scancode table */
for (j = 0; j < 256; j++) {
for (i = 0; i < 256; i++) {
codes[0] = (j << 8) | i;
if (!ioctl(fd, EVIOCGKEYCODE, codes) && codes[1] != KEY_RESERVED)
prtcode(codes);
}
}
return 0;
}
요약·해설
keytable.c.rst:1-176이 예제는 단일 대응 설정, 파일 기반 전체 교체, 현재 표 조회를 명령행 인자에 따라 수행합니다. 모든 16-bit scan code 초기화와 symbolic key name 해석 과정을 실제 input ioctl 호출로 보여 줍니다.