요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programming gameport drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A basic classic gameport
~~~~~~~~~~~~~~~~~~~~~~~~
If the gameport doesn't provide more than the inb()/outb() functionality,
the code needed to register it with the joystick drivers is simple::
struct gameport gameport;
gameport.io = MY_IO_ADDRESS;
gameport_register_port(&gameport);
Make sure struct gameport is initialized to 0 in all other fields. The
gameport generic code will take care of the rest.
If your hardware supports more than one io address, and your driver can
choose which one to program the hardware to, starting from the more exotic
addresses is preferred, because the likelihood of clashing with the standard
0x201 address is smaller.
E.g. if your driver supports addresses 0x200, 0x208, 0x210 and 0x218, then
0x218 would be the address of first choice.
If your hardware supports a gameport address that is not mapped to ISA io
space (is above 0x1000), use that one, and don't map the ISA mirror.
Also, always request_region() on the whole io space occupied by the
gameport. Although only one ioport is really used, the gameport usually
occupies from one to sixteen addresses in the io space.
Please also consider enabling the gameport on the card in the ->open()
callback if the io is mapped to ISA space - this way it'll occupy the io
space only when something really is using it. Disable it again in the
->close() callback. You also can select the io address in the ->open()
callback, so that it doesn't fail if some of the possible addresses are
already occupied by other gameports.
Memory mapped gameport
~~~~~~~~~~~~~~~~~~~~~~
When a gameport can be accessed through MMIO, this way is preferred, because
it is faster, allowing more reads per second. Registering such a gameport
isn't as easy as a basic IO one, but not so much complex::
struct gameport gameport;
void my_trigger(struct gameport *gameport)
{
my_mmio = 0xff;
}
unsigned char my_read(struct gameport *gameport)
{
return my_mmio;
}
gameport.read = my_read;
gameport.trigger = my_trigger;
gameport_register_port(&gameport);
.. _gameport_pgm_cooked_mode:
Cooked mode gameport
~~~~~~~~~~~~~~~~~~~~
There are gameports that can report the axis values as numbers, that means
the driver doesn't have to measure them the old way - an ADC is built into
the gameport. To register a cooked gameport::
struct gameport gameport;
int my_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
int i;
for (i = 0; i < 4; i++)
axes[i] = my_mmio[i];
buttons[0] = my_mmio[4];
}
int my_open(struct gameport *gameport, int mode)
{
return -(mode != GAMEPORT_MODE_COOKED);
}
gameport.cooked_read = my_cooked_read;
gameport.open = my_open;
gameport.fuzz = 8;
gameport_register_port(&gameport);
The only confusing thing here is the fuzz value. Best determined by
experimentation, it is the amount of noise in the ADC data. Perfect
gameports can set this to zero, most common have fuzz between 8 and 32.
See analog.c and input.c for handling of fuzz - the fuzz value determines
the size of a gaussian filter window that is used to eliminate the noise
in the data.
More complex gameports
~~~~~~~~~~~~~~~~~~~~~~
Gameports can support both raw and cooked modes. In that case combine either
examples 1+2 or 1+3. Gameports can support internal calibration - see below,
and also lightning.c and analog.c on how that works. If your driver supports
more than one gameport instance simultaneously, use the ->private member of
the gameport struct to point to your data.
Unregistering a gameport
~~~~~~~~~~~~~~~~~~~~~~~~
Simple::
gameport_unregister_port(&gameport);
The gameport structure
~~~~~~~~~~~~~~~~~~~~~~
::
struct gameport {
void *port_data;
A private pointer for free use in the gameport driver. (Not the joystick
driver!)
::
char name[32];
Driver's name as set by driver calling gameport_set_name(). Informational
purpose only.
::
char phys[32];
gameport's physical name/description as set by driver calling gameport_set_phys().
Informational purpose only.
::
int io;
I/O address for use with raw mode. You have to either set this, or ->read()
to some value if your gameport supports raw mode.
::
int speed;
Raw mode speed of the gameport reads in thousands of reads per second.
::
int fuzz;
If the gameport supports cooked mode, this should be set to a value that
represents the amount of noise in the data. See
:ref:`gameport_pgm_cooked_mode`.
::
void (*trigger)(struct gameport *);
Trigger. This function should trigger the ns558 oneshots. If set to NULL,
outb(0xff, io) will be used.
::
unsigned char (*read)(struct gameport *);
Read the buttons and ns558 oneshot bits. If set to NULL, inb(io) will be
used instead.
::
int (*cooked_read)(struct gameport *, int *axes, int *buttons);
If the gameport supports cooked mode, it should point this to its cooked
read function. It should fill axes[0..3] with four values of the joystick axes
and buttons[0] with four bits representing the buttons.
::
int (*calibrate)(struct gameport *, int *axes, int *max);
Function for calibrating the ADC hardware. When called, axes[0..3] should be
pre-filled by cooked data by the caller, max[0..3] should be pre-filled with
expected maximums for each axis. The calibrate() function should set the
sensitivity of the ADC hardware so that the maximums fit in its range and
recompute the axes[] values to match the new sensitivity or re-read them from
the hardware so that they give valid values.
::
int (*open)(struct gameport *, int mode);
Open() serves two purposes. First a driver either opens the port in raw or
in cooked mode, the open() callback can decide which modes are supported.
Second, resource allocation can happen here. The port can also be enabled
here. Prior to this call, other fields of the gameport struct (namely the io
member) need not to be valid.
::
void (*close)(struct gameport *);
Close() should free the resources allocated by open, possibly disabling the
gameport.
::
struct timer_list poll_timer;
unsigned int poll_interval; /* in msecs */
spinlock_t timer_lock;
unsigned int poll_cnt;
void (*poll_handler)(struct gameport *);
struct gameport *parent, *child;
struct gameport_driver *drv;
struct mutex drv_mutex; /* protects serio->drv so attributes can pin driver */
struct device dev;
struct list_head node;
For internal use by the gameport layer.
::
};
Enjoy!
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
기본 classic gameport 등록과 I/O 자원
1-40Gameport가 `inb()`와 `outb()` 기능만 제공한다면 `struct gameport`를 0으로 초기화하고 `io`에 주소를 넣은 뒤 `gameport_register_port()`를 호출하면 됩니다. 나머지는 generic gameport code가 처리합니다.
Classic I/O port 방식에 필요한 최소 설정입니다.
Hardware가 여러 I/O 주소를 지원하고 driver가 선택할 수 있다면 표준 `0x201`과 충돌할 가능성이 낮은 비표준 주소부터 시도합니다. 예를 들어 `0x200`, `0x208`, `0x210`, `0x218`을 지원하면 `0x218`을 첫 선택으로 삼습니다.
ISA I/O 공간에 매핑되지 않은 `0x1000` 초과 주소를 지원하면 그 주소를 사용하고 ISA mirror는 매핑하지 않습니다. 실제로 한 I/O port만 읽더라도 gameport가 1~16개 주소를 점유할 수 있으므로 전체 범위에 `request_region()`을 호출해야 합니다.
ISA 공간의 gameport는 `->open()`에서 card 기능을 켜 실제 사용 중일 때만 I/O 공간을 점유하고 `->close()`에서 다시 끄는 방식을 고려합니다. `->open()`에서 후보 주소를 선택하면 일부 주소가 다른 gameport에 점유돼도 다른 주소로 열 수 있습니다.
주소 선택부터 사용 종료까지의 자원 생명주기입니다.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programming gameport drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A basic classic gameport
~~~~~~~~~~~~~~~~~~~~~~~~
If the gameport doesn't provide more than the inb()/outb() functionality,
the code needed to register it with the joystick drivers is simple::
struct gameport gameport;
gameport.io = MY_IO_ADDRESS;
gameport_register_port(&gameport);
Make sure struct gameport is initialized to 0 in all other fields. The
gameport generic code will take care of the rest.
If your hardware supports more than one io address, and your driver can
choose which one to program the hardware to, starting from the more exotic
addresses is preferred, because the likelihood of clashing with the standard
0x201 address is smaller.
E.g. if your driver supports addresses 0x200, 0x208, 0x210 and 0x218, then
0x218 would be the address of first choice.
If your hardware supports a gameport address that is not mapped to ISA io
space (is above 0x1000), use that one, and don't map the ISA mirror.
Also, always request_region() on the whole io space occupied by the
gameport. Although only one ioport is really used, the gameport usually
occupies from one to sixteen addresses in the io space.
Please also consider enabling the gameport on the card in the ->open()
callback if the io is mapped to ISA space - this way it'll occupy the io
space only when something really is using it. Disable it again in the
->close() callback. You also can select the io address in the ->open()
callback, so that it doesn't fail if some of the possible addresses are
already occupied by other gameports.
Memory-mapped gameport
41-65MMIO로 접근할 수 있는 gameport는 초당 더 많은 read가 가능해 더 빠르므로 이 방식을 우선합니다. 기본 I/O 방식보다 callback 설정이 더 필요하지만 `read`와 `trigger`를 제공하면 등록할 수 있습니다.
예제의 `my_trigger()`는 MMIO register에 `0xff`를 써 one-shot 측정을 시작하고, `my_read()`는 register 값을 반환합니다. 두 함수를 `gameport.trigger`와 `gameport.read`에 넣은 뒤 `gameport_register_port()`를 호출합니다.
Port I/O 기본 동작을 memory access 함수로 대체합니다.
Trigger와 read callback이 raw port 동작을 대신합니다.
Memory mapped gameport
~~~~~~~~~~~~~~~~~~~~~~
When a gameport can be accessed through MMIO, this way is preferred, because
it is faster, allowing more reads per second. Registering such a gameport
isn't as easy as a basic IO one, but not so much complex::
struct gameport gameport;
void my_trigger(struct gameport *gameport)
{
my_mmio = 0xff;
}
unsigned char my_read(struct gameport *gameport)
{
return my_mmio;
}
gameport.read = my_read;
gameport.trigger = my_trigger;
gameport_register_port(&gameport);
.. _gameport_pgm_cooked_mode:
Cooked mode와 ADC noise
66-100일부 gameport는 ADC를 내장해 driver가 옛 방식으로 시간을 측정하지 않아도 axis 값을 숫자로 직접 보고합니다. 이런 장치는 `cooked_read` callback에서 `axes[0..3]`과 button bits를 채우고 `open`에서 `GAMEPORT_MODE_COOKED`만 허용합니다.
예제 `my_cooked_read()`는 MMIO의 네 axis 값을 `axes[]`에, button 값을 `buttons[0]`에 넣습니다. `my_open()`은 mode가 cooked가 아니면 오류를 반환합니다. `cooked_read`, `open`, `fuzz`를 설정한 뒤 port를 등록합니다.
ADC 값을 직접 제공하는 gameport의 필수 callback과 보정값입니다.
`fuzz`는 ADC data의 noise 양이며 실험으로 결정하는 것이 가장 좋습니다. 완벽한 gameport는 0, 일반적인 장치는 8~32 정도입니다. `analog.c`와 `input.c`에서처럼 이 값은 noise 제거용 Gaussian filter window 크기를 결정합니다.
ADC sample을 input 값으로 안정화하는 경로입니다.
Cooked mode gameport
~~~~~~~~~~~~~~~~~~~~
There are gameports that can report the axis values as numbers, that means
the driver doesn't have to measure them the old way - an ADC is built into
the gameport. To register a cooked gameport::
struct gameport gameport;
int my_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
int i;
for (i = 0; i < 4; i++)
axes[i] = my_mmio[i];
buttons[0] = my_mmio[4];
}
int my_open(struct gameport *gameport, int mode)
{
return -(mode != GAMEPORT_MODE_COOKED);
}
gameport.cooked_read = my_cooked_read;
gameport.open = my_open;
gameport.fuzz = 8;
gameport_register_port(&gameport);
The only confusing thing here is the fuzz value. Best determined by
experimentation, it is the amount of noise in the ADC data. Perfect
gameports can set this to zero, most common have fuzz between 8 and 32.
See analog.c and input.c for handling of fuzz - the fuzz value determines
the size of a gaussian filter window that is used to eliminate the noise
in the data.
복합 모드, calibration과 등록 해제
101-116Gameport는 raw와 cooked mode를 함께 지원할 수 있습니다. 이 경우 classic 예제와 MMIO 또는 cooked 예제를 결합합니다. 내부 calibration도 지원할 수 있으며 동작 방식은 아래 설명과 `lightning.c`, `analog.c`를 참고합니다.
Driver가 여러 gameport instance를 동시에 지원하면 각 `struct gameport`의 `->private` 계열 private pointer를 사용해 instance별 data를 연결합니다.
Gameport 등록을 해제할 때는 `gameport_unregister_port(&gameport)`를 호출합니다.
지원 기능별로 결합할 요소를 정리했습니다.
등록한 gameport를 core에서 안전하게 분리합니다.
More complex gameports
~~~~~~~~~~~~~~~~~~~~~~
Gameports can support both raw and cooked modes. In that case combine either
examples 1+2 or 1+3. Gameports can support internal calibration - see below,
and also lightning.c and analog.c on how that works. If your driver supports
more than one gameport instance simultaneously, use the ->private member of
the gameport struct to point to your data.
Unregistering a gameport
~~~~~~~~~~~~~~~~~~~~~~~~
Simple::
gameport_unregister_port(&gameport);
`struct gameport`의 공개 필드와 callback
117-196`port_data`는 gameport driver가 자유롭게 사용하는 private pointer이며 joystick driver용이 아닙니다. `name[32]`는 `gameport_set_name()`으로 설정하는 driver 이름, `phys[32]`는 `gameport_set_phys()`로 설정하는 물리적 이름 또는 설명으로 둘 다 정보 제공용입니다.
`io`는 raw mode의 I/O 주소입니다. Raw mode를 지원하려면 `io` 또는 `->read()` 중 하나를 유효하게 설정해야 합니다. `speed`는 초당 천 회 단위로 나타낸 raw read 속도입니다.
`fuzz`는 cooked mode ADC data의 noise 양을 나타냅니다. `trigger` callback은 ns558 one-shot을 시작하며 `NULL`이면 core가 `outb(0xff, io)`를 사용합니다. `read` callback은 button과 ns558 one-shot bits를 읽으며 `NULL`이면 `inb(io)`를 사용합니다.
`cooked_read`는 `axes[0..3]`에 네 joystick axis 값을, `buttons[0]`에 네 button bit를 채웁니다. `calibrate`는 caller가 미리 채운 cooked `axes[0..3]`와 예상 최대값 `max[0..3]`을 받아 ADC sensitivity를 범위에 맞게 조정하고 새 sensitivity에 맞는 유효 axis 값을 다시 계산하거나 읽어야 합니다.
Gameport driver가 설정하는 데이터와 callback입니다.
`calibrate()`가 기존 sample과 예상 최대값을 새 sensitivity에 맞춥니다.
The gameport structure
~~~~~~~~~~~~~~~~~~~~~~
::
struct gameport {
void *port_data;
A private pointer for free use in the gameport driver. (Not the joystick
driver!)
::
char name[32];
Driver's name as set by driver calling gameport_set_name(). Informational
purpose only.
::
char phys[32];
gameport's physical name/description as set by driver calling gameport_set_phys().
Informational purpose only.
::
int io;
I/O address for use with raw mode. You have to either set this, or ->read()
to some value if your gameport supports raw mode.
::
int speed;
Raw mode speed of the gameport reads in thousands of reads per second.
::
int fuzz;
If the gameport supports cooked mode, this should be set to a value that
represents the amount of noise in the data. See
:ref:`gameport_pgm_cooked_mode`.
::
void (*trigger)(struct gameport *);
Trigger. This function should trigger the ns558 oneshots. If set to NULL,
outb(0xff, io) will be used.
::
unsigned char (*read)(struct gameport *);
Read the buttons and ns558 oneshot bits. If set to NULL, inb(io) will be
used instead.
::
int (*cooked_read)(struct gameport *, int *axes, int *buttons);
If the gameport supports cooked mode, it should point this to its cooked
read function. It should fill axes[0..3] with four values of the joystick axes
and buttons[0] with four bits representing the buttons.
::
int (*calibrate)(struct gameport *, int *axes, int *max);
Function for calibrating the ADC hardware. When called, axes[0..3] should be
pre-filled by cooked data by the caller, max[0..3] should be pre-filled with
expected maximums for each axis. The calibrate() function should set the
sensitivity of the ADC hardware so that the maximums fit in its range and
recompute the axes[] values to match the new sensitivity or re-read them from
the hardware so that they give valid values.
`open`, `close`와 gameport core 내부 필드
197-233`open(struct gameport *, int mode)`은 두 역할을 합니다. 먼저 raw 또는 cooked mode 요청을 받아 지원 여부를 결정하고, 이어 자원을 할당하거나 port를 활성화할 수 있습니다. 이 호출 전에는 `io`를 포함한 다른 `struct gameport` 필드가 유효하지 않아도 됩니다.
`close(struct gameport *)`는 `open`이 할당한 자원을 해제하고 필요하면 gameport를 비활성화해야 합니다.
Mode 선택과 자원 관리를 callback 경계에 묶습니다.
`poll_timer`, `poll_interval`, `timer_lock`, `poll_cnt`, `poll_handler`, `parent`, `child`, `drv`, `drv_mutex`, `dev`, `node`는 gameport layer 내부에서 사용합니다. Driver는 이 내부 관리 필드를 직접 운용하지 않습니다.
Polling, 계층 관계, driver binding과 device model 상태를 보관합니다.
Core가 mode를 열고 닫는 동안 driver 자원을 관리합니다.
::
int (*open)(struct gameport *, int mode);
Open() serves two purposes. First a driver either opens the port in raw or
in cooked mode, the open() callback can decide which modes are supported.
Second, resource allocation can happen here. The port can also be enabled
here. Prior to this call, other fields of the gameport struct (namely the io
member) need not to be valid.
::
void (*close)(struct gameport *);
Close() should free the resources allocated by open, possibly disabling the
gameport.
::
struct timer_list poll_timer;
unsigned int poll_interval; /* in msecs */
spinlock_t timer_lock;
unsigned int poll_cnt;
void (*poll_handler)(struct gameport *);
struct gameport *parent, *child;
struct gameport_driver *drv;
struct mutex drv_mutex; /* protects serio->drv so attributes can pin driver */
struct device dev;
struct list_head node;
For internal use by the gameport layer.
::
};
Enjoy!
요약·해설
gameport-programming.rst:1-233Gameport driver가 raw I/O, MMIO, cooked ADC mode를 제공하는 방식과 `struct gameport`의 callback, calibration, open·close, 등록·해제 생명주기를 설명합니다.
Hardware 접근 방식에 따라 설정할 필드를 구분합니다.
Hardware capability에서 callback과 자원 관리 방식이 결정됩니다.