Documentation/driver-api/isa.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

ISA Drivers

Non-discoverable ISA hardware를 위한 driver-owned device creation, match·probe 정책과 registration rollback model을 설명합니다.

Source pathDocumentation/driver-api/isa.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

isa.rst:1-122

ISA bus model은 자동 discovery가 불가능한 legacy hardware에서 driver가 device를 내부 생성하고 `.match()`로 prerequisite 또는 실제 존재 여부를 판단하게 합니다. Match가 하나도 없거나 오류가 나면 registration 전체를 rollback하므로 module load 결과가 hardware 상태를 정확히 반영합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 ===========
2 ISA Drivers
3 ===========
4
5 The following text is adapted from the commit message of the initial
6 commit of the ISA bus driver authored by Rene Herman.
7
8 During the recent "isa drivers using platform devices" discussion it was
9 pointed out that (ALSA) ISA drivers ran into the problem of not having
10 the option to fail driver load (device registration rather) upon not
11 finding their hardware due to a probe() error not being passed up
12 through the driver model. In the course of that, I suggested a separate
13 ISA bus might be best; Russell King agreed and suggested this bus could
14 use the .match() method for the actual device discovery.
15
16 The attached does this. For this old non (generically) discoverable ISA
17 hardware only the driver itself can do discovery so as a difference with
18 the platform_bus, this isa_bus also distributes match() up to the
19 driver.
20
21 As another difference: these devices only exist in the driver model due
22 to the driver creating them because it might want to drive them, meaning
23 that all device creation has been made internal as well.
24
25 The usage model this provides is nice, and has been acked from the ALSA
26 side by Takashi Iwai and Jaroslav Kysela. The ALSA driver module_init's
27 now (for oldisa-only drivers) become::
28
29 static int __init alsa_card_foo_init(void)
30 {
31 return isa_register_driver(&snd_foo_isa_driver, SNDRV_CARDS);
32 }
33
34 static void __exit alsa_card_foo_exit(void)
35 {
36 isa_unregister_driver(&snd_foo_isa_driver);
37 }
38
39 Quite like the other bus models therefore. This removes a lot of
40 duplicated init code from the ALSA ISA drivers.
41
42 The passed in isa_driver struct is the regular driver struct embedding a
43 struct device_driver, the normal probe/remove/shutdown/suspend/resume
44 callbacks, and as indicated that .match callback.
45
46 The "SNDRV_CARDS" you see being passed in is a "unsigned int ndev"
47 parameter, indicating how many devices to create and call our methods
48 with.
49
50 The platform_driver callbacks are called with a platform_device param;
51 the isa_driver callbacks are being called with a ``struct device *dev,
52 unsigned int id`` pair directly -- with the device creation completely
53 internal to the bus it's much cleaner to not leak isa_dev's by passing
54 them in at all. The id is the only thing we ever want other then the
55 struct device anyways, and it makes for nicer code in the callbacks as
56 well.
57
58 With this additional .match() callback ISA drivers have all options. If
59 ALSA would want to keep the old non-load behaviour, it could stick all
60 of the old .probe in .match, which would only keep them registered after
61 everything was found to be present and accounted for. If it wanted the
62 behaviour of always loading as it inadvertently did for a bit after the
63 changeover to platform devices, it could just not provide a .match() and
64 do everything in .probe() as before.
65
66 If it, as Takashi Iwai already suggested earlier as a way of following
67 the model from saner buses more closely, wants to load when a later bind
68 could conceivably succeed, it could use .match() for the prerequisites
69 (such as checking the user wants the card enabled and that port/irq/dma
70 values have been passed in) and .probe() for everything else. This is
71 the nicest model.
72
73 To the code...
74
75 This exports only two functions; isa_{,un}register_driver().
76
77 isa_register_driver() register's the struct device_driver, and then
78 loops over the passed in ndev creating devices and registering them.
79 This causes the bus match method to be called for them, which is::
80
81 int isa_bus_match(struct device *dev, struct device_driver *driver)
82 {
83 struct isa_driver *isa_driver = to_isa_driver(driver);
84
85 if (dev->platform_data == isa_driver) {
86 if (!isa_driver->match ||
87 isa_driver->match(dev, to_isa_dev(dev)->id))
88 return 1;
89 dev->platform_data = NULL;
90 }
91 return 0;
92 }
93
94 The first thing this does is check if this device is in fact one of this
95 driver's devices by seeing if the device's platform_data pointer is set
96 to this driver. Platform devices compare strings, but we don't need to
97 do that with everything being internal, so isa_register_driver() abuses
98 dev->platform_data as a isa_driver pointer which we can then check here.
99 I believe platform_data is available for this, but if rather not, moving
100 the isa_driver pointer to the private struct isa_dev is ofcourse fine as
101 well.
102
103 Then, if the driver did not provide a .match, it matches. If it did,
104 the driver match() method is called to determine a match.
105
106 If it did **not** match, dev->platform_data is reset to indicate this to
107 isa_register_driver which can then unregister the device again.
108
109 If during all this, there's any error, or no devices matched at all
110 everything is backed out again and the error, or -ENODEV, is returned.
111
112 isa_unregister_driver() just unregisters the matched devices and the
113 driver itself.
114
115 module_isa_driver is a helper macro for ISA drivers which do not do
116 anything special in module init/exit. This eliminates a lot of
117 boilerplate code. Each module may only use this macro once, and calling
118 it replaces module_init and module_exit.
119
120 max_num_isa_dev is a macro to determine the maximum possible number of
121 ISA devices which may be registered in the I/O port address space given
122 the address extent of the ISA devices.
123

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

ISA bus driver 도입 배경

1-24

문서 제목은 `ISA Drivers`입니다. 이 글은 Rene Herman이 작성한 초기 ISA bus driver commit message를 바탕으로 합니다.

ISA driver가 platform device를 사용하던 방식에서는 hardware를 찾지 못해 `probe()`가 실패해도 그 오류가 driver model 위로 전달되지 않아 ALSA ISA driver가 device registration, 즉 module load를 실패시킬 수 없는 문제가 있었습니다. 이에 별도 ISA bus를 두고 실제 device discovery에 `.match()` method를 사용하자는 설계가 제안되었습니다.

오래된 ISA hardware는 일반적으로 discoverable하지 않아서 driver 자신만 hardware를 찾을 수 있습니다. 따라서 `platform_bus`와 달리 `isa_bus`는 `match()`를 driver까지 전달합니다.

이 device들은 driver가 구동할 가능성이 있어 직접 생성할 때만 driver model에 존재합니다. 그 때문에 device creation도 ISA bus 내부로 감쌌습니다.

ISA discovery 문제와 해결
Non-discoverable ISA hardwarePlatform probe error가 module load까지 전달되지 않음별도 isa_bus 도입Driver .match()가 실제 discovery 수행No match면 registration rollback 가능

Platform-device 전환에서 잃었던 probe failure 전달을 ISA bus match로 복구합니다.

ALSA driver 등록 model

25-40

이 사용 model은 ALSA 측 Takashi Iwai와 Jaroslav Kysela가 동의했습니다. Old-ISA 전용 ALSA driver의 `module_init`과 `module_exit`는 각각 `isa_register_driver()`와 `isa_unregister_driver()` 호출만 남습니다.

static int __init alsa_card_foo_init(void)
{
        return isa_register_driver(&snd_foo_isa_driver, SNDRV_CARDS);
}

static void __exit alsa_card_foo_exit(void)
{
        isa_unregister_driver(&snd_foo_isa_driver);
}

다른 bus model과 유사한 lifecycle이 되며 ALSA ISA driver에 반복되던 initialization code를 많이 제거합니다.

ALSA ISA module lifecycle
module_initisa_register_driver(&snd_foo_isa_driver, SNDRV_CARDS)Internal device creation·matchingmodule_exitisa_unregister_driver(&snd_foo_isa_driver)

Module entry·exit를 ISA bus helper에 위임합니다.

기존 코드와 ISA helper
구성Initialization 책임
기존 platform-device 방식Driver마다 device 생성·등록 code 반복
ISA bus 방식isa_register_driver가 ndev만큼 생성·등록
Exitisa_unregister_driver가 matched device와 driver 정리

Driver별 boilerplate를 bus core가 맡습니다.

isa_driver와 callback 인자

41-56

전달하는 `struct isa_driver`는 일반 driver structure로, `struct device_driver`를 embed하고 표준 `probe`, `remove`, `shutdown`, `suspend`, `resume` callback과 ISA 전용 `.match` callback을 가집니다.

예시의 `SNDRV_CARDS`는 `unsigned int ndev` parameter입니다. 생성할 device 수와 callback을 호출할 횟수를 나타냅니다.

`platform_driver` callback은 `platform_device`를 받지만 `isa_driver` callback은 `struct device *dev, unsigned int id` pair를 직접 받습니다. Device creation이 bus 내부에 있으므로 private `isa_dev`를 외부에 노출하지 않는 편이 더 깔끔합니다. Callback이 실제로 필요한 것은 `struct device`와 `id`뿐이며 이 형태가 callback code도 단순하게 만듭니다.

Platform과 ISA callback 비교
Driver modelCallback parameter노출 범위
platform_driverstruct platform_device *platform_device 노출
isa_driverstruct device *dev, unsigned int idisa_dev는 bus 내부
ndevunsigned int생성·callback 대상 수

Bus가 내부 device type을 숨기는 차이입니다.

match와 probe 정책

57-72

추가된 `.match()` callback 덕분에 ISA driver는 load policy를 선택할 수 있습니다. 이전처럼 hardware가 모두 존재할 때만 등록을 유지하려면 옛 `.probe` logic 전체를 `.match`에 둘 수 있습니다.

Platform device 전환 직후처럼 hardware 여부와 관계없이 항상 load하려면 `.match()`를 제공하지 않고 기존처럼 `.probe()`에서 모든 작업을 수행할 수 있습니다.

더 정상적인 bus model을 따르려면 나중의 bind가 성공할 가능성이 있을 때 module을 load합니다. `.match()`에서는 사용자가 card를 enable했는지, port·IRQ·DMA 값이 제공되었는지 같은 prerequisite만 검사하고 실제 hardware setup은 `.probe()`가 맡습니다. 문서는 이 model을 가장 좋은 방식으로 권장합니다.

ISA load policy
정책.match().probe()결과
Strict presence전체 hardware discoverySetup모두 발견될 때만 등록
Always load제공하지 않음Discovery와 setup 전부항상 match 후 probe
권장 modelEnable·port·IRQ·DMA prerequisite나머지 discovery·setupLater bind 가능성을 유지

match와 probe의 분담에 따른 세 가지 동작입니다.

등록 API와 bus match 구현

73-92

ISA core가 export하는 function은 `isa_register_driver()`와 `isa_unregister_driver()` 두 개뿐입니다.

`isa_register_driver()`는 `struct device_driver`를 먼저 등록한 뒤 전달된 `ndev`만큼 반복해 device를 생성하고 등록합니다. 각 registration은 아래 ISA bus match method를 호출합니다.

int isa_bus_match(struct device *dev, struct device_driver *driver)
{
        struct isa_driver *isa_driver = to_isa_driver(driver);

        if (dev->platform_data == isa_driver) {
                if (!isa_driver->match ||
                        isa_driver->match(dev, to_isa_dev(dev)->id))
                        return 1;
                dev->platform_data = NULL;
        }
        return 0;
}
isa_register_driver 실행 순서
struct device_driver 등록id=0부터 ndev-1 순회Internal isa_dev 생성Device 등록isa_bus_match()Match 성공 device 유지다음 id 처리

Driver registration에서 per-ID match까지의 흐름입니다.

platform_data 기반 owner 확인

93-108

`isa_bus_match()`는 먼저 `dev->platform_data`가 현재 `isa_driver`를 가리키는지 확인해 이 device가 해당 driver가 만든 device인지 판별합니다. Platform device는 string을 비교하지만 ISA device 생성은 모두 내부에서 일어나므로 `isa_register_driver()`가 `dev->platform_data`를 `isa_driver` pointer 저장소로 사용하고 match에서 직접 비교합니다.

문서는 `platform_data`를 이 용도로 사용할 수 있다고 보며, 원하지 않는다면 pointer를 private `struct isa_dev`로 옮겨도 된다고 설명합니다.

Driver가 `.match`를 제공하지 않으면 즉시 match합니다. 제공했다면 `isa_driver->match(dev, id)` 결과로 결정합니다. Match하지 않으면 `dev->platform_data`를 `NULL`로 reset해 `isa_register_driver()`가 해당 device를 다시 unregister할 수 있게 알립니다.

isa_bus_match decision
dev->platform_data == isa_driver?아니오: return 0예: .match callback이 있는가?없음: return 1있음: match(dev, id)성공: return 1실패: platform_data=NULL 후 return 0

Device ownership과 optional driver match를 차례로 검사합니다.

Rollback·unregister·helper macro

109-122

등록 과정에서 오류가 발생하거나 device가 하나도 match하지 않으면 지금까지의 작업을 모두 되돌리고 원래 error 또는 `-ENODEV`를 반환합니다.

`isa_unregister_driver()`는 match된 device를 unregister한 뒤 driver 자체를 unregister합니다.

`module_isa_driver`는 module init/exit에서 특별한 작업을 하지 않는 ISA driver용 helper macro입니다. Boilerplate를 제거하며 module 하나에서 한 번만 사용할 수 있고 `module_init`과 `module_exit`를 대체합니다.

`max_num_isa_dev`는 ISA device의 address extent를 기준으로 I/O port address space에 등록할 수 있는 ISA device 최대 수를 계산하는 macro입니다.

ISA registration 종료 조건
항목동작
오류 발생생성·등록 state 전부 rollback 후 error 반환
Match 0개전부 rollback 후 -ENODEV
isa_unregister_driverMatched device와 driver unregister
module_isa_driverinit·exit boilerplate 대체, module당 1회
max_num_isa_devAddress extent로 최대 device 수 계산

성공·실패·module helper 동작을 정리했습니다.