요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=======================================
Porting Drivers to the New Driver Model
=======================================
Patrick Mochel
7 January 2003
Overview
Please refer to `Documentation/driver-api/driver-model/*.rst` for definitions of
various driver types and concepts.
Most of the work of porting devices drivers to the new model happens
at the bus driver layer. This was intentional, to minimize the
negative effect on kernel drivers, and to allow a gradual transition
of bus drivers.
In a nutshell, the driver model consists of a set of objects that can
be embedded in larger, bus-specific objects. Fields in these generic
objects can replace fields in the bus-specific objects.
The generic objects must be registered with the driver model core. By
doing so, they will exported via the sysfs filesystem. sysfs can be
mounted by doing::
# mount -t sysfs sysfs /sys
The Process
Step 0: Read include/linux/device.h for object and function definitions.
Step 1: Registering the bus driver.
- Define a struct bus_type for the bus driver::
struct bus_type pci_bus_type = {
.name = "pci",
};
- Register the bus type.
This should be done in the initialization function for the bus type,
which is usually the module_init(), or equivalent, function::
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);
}
subsys_initcall(pci_driver_init);
The bus type may be unregistered (if the bus driver may be compiled
as a module) by doing::
bus_unregister(&pci_bus_type);
- Export the bus type for others to use.
Other code may wish to reference the bus type, so declare it in a
shared header file and export the symbol.
From include/linux/pci.h::
extern struct bus_type pci_bus_type;
From file the above code appears in::
EXPORT_SYMBOL(pci_bus_type);
- This will cause the bus to show up in /sys/bus/pci/ with two
subdirectories: 'devices' and 'drivers'::
# tree -d /sys/bus/pci/
/sys/bus/pci/
|-- devices
`-- drivers
Step 2: Registering Devices.
struct device represents a single device. It mainly contains metadata
describing the relationship the device has to other entities.
- Embed a struct device in the bus-specific device type::
struct pci_dev {
...
struct device dev; /* Generic device interface */
...
};
It is recommended that the generic device not be the first item in
the struct to discourage programmers from doing mindless casts
between the object types. Instead macros, or inline functions,
should be created to convert from the generic object type::
#define to_pci_dev(n) container_of(n, struct pci_dev, dev)
or
static inline struct pci_dev * to_pci_dev(struct kobject * kobj)
{
return container_of(n, struct pci_dev, dev);
}
This allows the compiler to verify type-safety of the operations
that are performed (which is Good).
- Initialize the device on registration.
When devices are discovered or registered with the bus type, the
bus driver should initialize the generic device. The most important
things to initialize are the bus_id, parent, and bus fields.
The bus_id is an ASCII string that contains the device's address on
the bus. The format of this string is bus-specific. This is
necessary for representing devices in sysfs.
parent is the physical parent of the device. It is important that
the bus driver sets this field correctly.
The driver model maintains an ordered list of devices that it uses
for power management. This list must be in order to guarantee that
devices are shutdown before their physical parents, and vice versa.
The order of this list is determined by the parent of registered
devices.
Also, the location of the device's sysfs directory depends on a
device's parent. sysfs exports a directory structure that mirrors
the device hierarchy. Accurately setting the parent guarantees that
sysfs will accurately represent the hierarchy.
The device's bus field is a pointer to the bus type the device
belongs to. This should be set to the bus_type that was declared
and initialized before.
Optionally, the bus driver may set the device's name and release
fields.
The name field is an ASCII string describing the device, like
"ATI Technologies Inc Radeon QD"
The release field is a callback that the driver model core calls
when the device has been removed, and all references to it have
been released. More on this in a moment.
- Register the device.
Once the generic device has been initialized, it can be registered
with the driver model core by doing::
device_register(&dev->dev);
It can later be unregistered by doing::
device_unregister(&dev->dev);
This should happen on buses that support hotpluggable devices.
If a bus driver unregisters a device, it should not immediately free
it. It should instead wait for the driver model core to call the
device's release method, then free the bus-specific object.
(There may be other code that is currently referencing the device
structure, and it would be rude to free the device while that is
happening).
When the device is registered, a directory in sysfs is created.
The PCI tree in sysfs looks like::
/sys/devices/pci0/
|-- 00:00.0
|-- 00:01.0
| `-- 01:00.0
|-- 00:02.0
| `-- 02:1f.0
| `-- 03:00.0
|-- 00:1e.0
| `-- 04:04.0
|-- 00:1f.0
|-- 00:1f.1
| |-- ide0
| | |-- 0.0
| | `-- 0.1
| `-- ide1
| `-- 1.0
|-- 00:1f.2
|-- 00:1f.3
`-- 00:1f.5
Also, symlinks are created in the bus's 'devices' directory
that point to the device's directory in the physical hierarchy::
/sys/bus/pci/devices/
|-- 00:00.0 -> ../../../devices/pci0/00:00.0
|-- 00:01.0 -> ../../../devices/pci0/00:01.0
|-- 00:02.0 -> ../../../devices/pci0/00:02.0
|-- 00:1e.0 -> ../../../devices/pci0/00:1e.0
|-- 00:1f.0 -> ../../../devices/pci0/00:1f.0
|-- 00:1f.1 -> ../../../devices/pci0/00:1f.1
|-- 00:1f.2 -> ../../../devices/pci0/00:1f.2
|-- 00:1f.3 -> ../../../devices/pci0/00:1f.3
|-- 00:1f.5 -> ../../../devices/pci0/00:1f.5
|-- 01:00.0 -> ../../../devices/pci0/00:01.0/01:00.0
|-- 02:1f.0 -> ../../../devices/pci0/00:02.0/02:1f.0
|-- 03:00.0 -> ../../../devices/pci0/00:02.0/02:1f.0/03:00.0
`-- 04:04.0 -> ../../../devices/pci0/00:1e.0/04:04.0
Step 3: Registering Drivers.
struct device_driver is a simple driver structure that contains a set
of operations that the driver model core may call.
- Embed a struct device_driver in the bus-specific driver.
Just like with devices, do something like::
struct pci_driver {
...
struct device_driver driver;
};
- Initialize the generic driver structure.
When the driver registers with the bus (e.g. doing pci_register_driver()),
initialize the necessary fields of the driver: the name and bus
fields.
- Register the driver.
After the generic driver has been initialized, call::
driver_register(&drv->driver);
to register the driver with the core.
When the driver is unregistered from the bus, unregister it from the
core by doing::
driver_unregister(&drv->driver);
Note that this will block until all references to the driver have
gone away. Normally, there will not be any.
- Sysfs representation.
Drivers are exported via sysfs in their bus's 'driver's directory.
For example::
/sys/bus/pci/drivers/
|-- 3c59x
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
|-- e100
`-- serial
Step 4: Define Generic Methods for Drivers.
struct device_driver defines a set of operations that the driver model
core calls. Most of these operations are probably similar to
operations the bus already defines for drivers, but taking different
parameters.
It would be difficult and tedious to force every driver on a bus to
simultaneously convert their drivers to generic format. Instead, the
bus driver should define single instances of the generic methods that
forward call to the bus-specific drivers. For instance::
static int pci_device_remove(struct device * dev)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv) {
if (drv->remove)
drv->remove(pci_dev);
pci_dev->driver = NULL;
}
return 0;
}
The generic driver should be initialized with these methods before it
is registered::
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.probe = pci_device_probe;
drv->driver.resume = pci_device_resume;
drv->driver.suspend = pci_device_suspend;
drv->driver.remove = pci_device_remove;
/* register with core */
driver_register(&drv->driver);
Ideally, the bus should only initialize the fields if they are not
already set. This allows the drivers to implement their own generic
methods.
Step 5: Support generic driver binding.
The model assumes that a device or driver can be dynamically
registered with the bus at any time. When registration happens,
devices must be bound to a driver, or drivers must be bound to all
devices that it supports.
A driver typically contains a list of device IDs that it supports. The
bus driver compares these IDs to the IDs of devices registered with it.
The format of the device IDs, and the semantics for comparing them are
bus-specific, so the generic model does attempt to generalize them.
Instead, a bus may supply a method in struct bus_type that does the
comparison::
int (*match)(struct device * dev, struct device_driver * drv);
match should return positive value if the driver supports the device,
and zero otherwise. It may also return error code (for example
-EPROBE_DEFER) if determining that given driver supports the device is
not possible.
When a device is registered, the bus's list of drivers is iterated
over. bus->match() is called for each one until a match is found.
When a driver is registered, the bus's list of devices is iterated
over. bus->match() is called for each device that is not already
claimed by a driver.
When a device is successfully bound to a driver, device->driver is
set, the device is added to a per-driver list of devices, and a
symlink is created in the driver's sysfs directory that points to the
device's physical directory::
/sys/bus/pci/drivers/
|-- 3c59x
| `-- 00:0b.0 -> ../../../../devices/pci0/00:0b.0
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
| `-- 00:00.0 -> ../../../../devices/pci0/00:00.0
|-- e100
| `-- 00:0c.0 -> ../../../../devices/pci0/00:0c.0
`-- serial
This driver binding should replace the existing driver binding
mechanism the bus currently uses.
Step 6: Supply a hotplug callback.
Whenever a device is registered with the driver model core, the
userspace program /sbin/hotplug is called to notify userspace.
Users can define actions to perform when a device is inserted or
removed.
The driver model core passes several arguments to userspace via
environment variables, including
- ACTION: set to 'add' or 'remove'
- DEVPATH: set to the device's physical path in sysfs.
A bus driver may also supply additional parameters for userspace to
consume. To do this, a bus must implement the 'hotplug' method in
struct bus_type::
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
This is called immediately before /sbin/hotplug is executed.
Step 7: Cleaning up the bus driver.
The generic bus, device, and driver structures provide several fields
that can replace those defined privately to the bus driver.
- Device list.
struct bus_type contains a list of all devices registered with the bus
type. This includes all devices on all instances of that bus type.
An internal list that the bus uses may be removed, in favor of using
this one.
The core provides an iterator to access these devices::
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *));
- Driver list.
struct bus_type also contains a list of all drivers registered with
it. An internal list of drivers that the bus driver maintains may
be removed in favor of using the generic one.
The drivers may be iterated over, like devices::
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
Please see drivers/base/bus.c for more information.
- rwsem
struct bus_type contains an rwsem that protects all core accesses to
the device and driver lists. This can be used by the bus driver
internally, and should be used when accessing the device or driver
lists the bus maintains.
- Device and driver fields.
Some of the fields in struct device and struct device_driver duplicate
fields in the bus-specific representations of these objects. Feel free
to remove the bus-specific ones and favor the generic ones. Note
though, that this will likely mean fixing up all the drivers that
reference the bus-specific fields (though those should all be 1-line
changes).
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
문서 정보와 개요
1-31문서 제목은 `Porting Drivers to the New Driver Model`이며 Patrick Mochel이 작성했습니다. 작성일은 2003년 1월 7일(`7 January 2003`)입니다.
여러 driver type과 개념의 정의는 `Documentation/driver-api/driver-model/*.rst`를 참고하십시오.
device driver를 새 model로 이식하는 작업은 대부분 bus driver layer에서 이루어집니다. kernel driver에 미치는 부정적 영향을 최소화하고 bus driver를 점진적으로 전환할 수 있도록 의도한 설계입니다.
요약하면 driver model은 더 큰 bus-specific object 안에 embed할 수 있는 generic object 집합으로 구성됩니다. generic object의 field가 bus-specific object의 기존 field를 대체할 수 있습니다.
generic object는 driver model core에 등록해야 합니다. 등록하면 sysfs filesystem을 통해 export됩니다. sysfs는 다음과 같이 mount할 수 있습니다.
# mount -t sysfs sysfs /sys
bus layer를 중심으로 generic object를 도입하는 전체 순서입니다.
Step 0-1: bus driver 등록
32-90Step 0에서는 object와 function 정의를 확인하기 위해 `include/linux/device.h`를 읽습니다.
Step 1에서는 bus driver를 등록합니다. 먼저 bus driver를 나타내는 `struct bus_type`을 정의합니다.
struct bus_type pci_bus_type = {
.name = "pci",
};
bus type은 해당 bus type의 initialization function에서 등록해야 합니다. 보통 `module_init()` 또는 이에 해당하는 함수이며, PCI 예제는 `subsys_initcall()`에서 `bus_register(&pci_bus_type)`을 호출합니다.
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);
}
subsys_initcall(pci_driver_init);
bus driver를 module로 compile할 수 있다면 bus type을 다음과 같이 unregister할 수 있습니다.
bus_unregister(&pci_bus_type);
다른 code가 bus type을 참조할 수 있으므로 shared header file에서 선언하고 symbol을 export해야 합니다. `include/linux/pci.h`의 선언은 다음과 같습니다.
extern struct bus_type pci_bus_type;
위 정의가 들어 있는 source file에서는 `pci_bus_type`을 export합니다.
EXPORT_SYMBOL(pci_bus_type);
이 등록으로 `/sys/bus/pci/` 아래에 `devices`와 `drivers` 두 subdirectory가 나타납니다.
# tree -d /sys/bus/pci/
/sys/bus/pci/
|-- devices
`-- drivers
정의·등록·공유·sysfs 노출의 대응 관계입니다.
Step 2: generic device 포함과 초기화
91-164`struct device`는 하나의 device를 나타내며, 주로 다른 entity와의 관계를 설명하는 metadata를 담습니다.
bus-specific device type 안에 `struct device`를 embed합니다. PCI의 `struct pci_dev`는 generic device interface인 `dev` member를 포함합니다.
struct pci_dev {
...
struct device dev; /* Generic device interface */
...
};
programmer가 object type 사이를 무심코 cast하지 않도록 generic device를 struct의 첫 member로 두지 않는 것이 좋습니다. 대신 generic object에서 bus-specific object로 변환하는 macro나 inline function을 만듭니다.
#define to_pci_dev(n) container_of(n, struct pci_dev, dev)
원문은 inline function 대안도 다음과 같이 제시합니다. 아래 block의 `kobj` parameter와 본문의 `n` 사용을 포함해 원문 표기를 그대로 보존했습니다.
static inline struct pci_dev * to_pci_dev(struct kobject * kobj)
{
return container_of(n, struct pci_dev, dev);
}
이 방식은 수행하는 operation의 type safety를 compiler가 검증하게 하며, 이는 바람직합니다.
device를 discover하거나 bus type에 등록할 때 bus driver가 generic device를 초기화해야 합니다. 가장 중요한 field는 `bus_id`, `parent`, `bus`입니다.
`bus_id`는 bus에서 device address를 나타내는 ASCII string이며 format은 bus-specific입니다. sysfs에서 device를 표현하려면 이 값이 필요합니다.
`parent`는 device의 물리적 parent입니다. driver model은 power management에 쓰는 ordered device list를 유지하며, child를 physical parent보다 먼저 shutdown하고 역방향 작업은 반대 순서로 수행하려면 정확한 순서가 필요합니다. 등록 device의 `parent`가 이 순서를 결정합니다.
device의 sysfs directory 위치도 `parent`에 좌우됩니다. sysfs는 device hierarchy를 반영하는 directory structure를 export하므로 `parent`를 정확히 설정해야 hierarchy도 정확히 표현됩니다.
device의 `bus` field는 자신이 속한 bus type을 가리키며, 앞서 선언하고 초기화한 `bus_type`으로 설정합니다. 선택적으로 `name`과 `release`도 설정할 수 있습니다.
`name`은 `ATI Technologies Inc Radeon QD`처럼 device를 설명하는 ASCII string입니다. `release`는 device가 제거되고 모든 reference가 해제된 뒤 driver model core가 호출하는 callback입니다.
등록 전에 설정할 핵심 관계와 선택 field입니다.
Step 2: device 등록과 sysfs hierarchy
165-227generic device 초기화가 끝나면 `device_register()`로 driver model core에 등록합니다.
device_register(&dev->dev);
나중에는 다음과 같이 unregister할 수 있습니다.
device_unregister(&dev->dev);
hotpluggable device를 지원하는 bus는 unregister를 수행해야 합니다. bus driver는 device를 unregister한 직후 memory를 free하면 안 됩니다. 다른 code가 device structure를 참조 중일 수 있으므로 core가 device의 `release` method를 호출할 때까지 기다린 뒤 bus-specific object를 free해야 합니다.
device를 등록하면 sysfs에 directory가 생성됩니다. PCI의 physical hierarchy는 다음 tree와 같습니다.
/sys/devices/pci0/
|-- 00:00.0
|-- 00:01.0
| `-- 01:00.0
|-- 00:02.0
| `-- 02:1f.0
| `-- 03:00.0
|-- 00:1e.0
| `-- 04:04.0
|-- 00:1f.0
|-- 00:1f.1
| |-- ide0
| | |-- 0.0
| | `-- 0.1
| `-- ide1
| `-- 1.0
|-- 00:1f.2
|-- 00:1f.3
`-- 00:1f.5
bus의 `devices` directory에는 physical hierarchy의 device directory를 가리키는 symlink도 생성됩니다.
/sys/bus/pci/devices/
|-- 00:00.0 -> ../../../devices/pci0/00:00.0
|-- 00:01.0 -> ../../../devices/pci0/00:01.0
|-- 00:02.0 -> ../../../devices/pci0/00:02.0
|-- 00:1e.0 -> ../../../devices/pci0/00:1e.0
|-- 00:1f.0 -> ../../../devices/pci0/00:1f.0
|-- 00:1f.1 -> ../../../devices/pci0/00:1f.1
|-- 00:1f.2 -> ../../../devices/pci0/00:1f.2
|-- 00:1f.3 -> ../../../devices/pci0/00:1f.3
|-- 00:1f.5 -> ../../../devices/pci0/00:1f.5
|-- 01:00.0 -> ../../../devices/pci0/00:01.0/01:00.0
|-- 02:1f.0 -> ../../../devices/pci0/00:02.0/02:1f.0
|-- 03:00.0 -> ../../../devices/pci0/00:02.0/02:1f.0/03:00.0
`-- 04:04.0 -> ../../../devices/pci0/00:1e.0/04:04.0
같은 device를 physical tree와 bus index에서 어떻게 표현하는지 구조화했습니다.
Step 3: driver 등록
228-280`struct device_driver`는 driver model core가 호출할 수 있는 operation 집합을 담는 단순한 driver structure입니다.
device와 마찬가지로 bus-specific driver 안에 `struct device_driver`를 embed합니다.
struct pci_driver {
...
struct device_driver driver;
};
driver가 bus에 등록될 때, 예를 들어 `pci_register_driver()`를 실행할 때 generic driver의 필수 field인 `name`과 `bus`를 초기화합니다.
generic driver를 초기화한 뒤 `driver_register()`로 core에 등록합니다.
driver_register(&drv->driver);
driver를 bus에서 unregister할 때는 core에서도 unregister합니다.
driver_unregister(&drv->driver);
`driver_unregister()`는 driver에 대한 모든 reference가 사라질 때까지 block합니다. 일반적으로 남은 reference는 없습니다.
driver는 해당 bus의 sysfs `drivers` directory를 통해 export됩니다. 예시는 다음과 같습니다.
/sys/bus/pci/drivers/
|-- 3c59x
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
|-- e100
`-- serial
bus-specific driver가 core object와 sysfs entry로 이어지는 과정입니다.
Step 4: driver용 generic method 정의
281-327`struct device_driver`에는 driver model core가 호출하는 operation 집합이 있습니다. 대부분 bus가 이미 driver에 정의한 operation과 비슷하지만 parameter type이 다릅니다.
한 bus의 모든 driver를 동시에 generic format으로 바꾸도록 강제하면 어렵고 지루합니다. 대신 bus driver가 generic method의 단일 instance를 정의하고 call을 bus-specific driver로 전달해야 합니다. 다음 `pci_device_remove()` wrapper는 generic `struct device`를 `struct pci_dev`로 변환한 뒤 bus-specific `remove`를 호출하고 `pci_dev->driver`를 비웁니다.
static int pci_device_remove(struct device * dev)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv) {
if (drv->remove)
drv->remove(pci_dev);
pci_dev->driver = NULL;
}
return 0;
}
generic driver를 등록하기 전에 common field와 `pci_device_probe`, `pci_device_resume`, `pci_device_suspend`, `pci_device_remove` callback으로 초기화합니다.
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.probe = pci_device_probe;
drv->driver.resume = pci_device_resume;
drv->driver.suspend = pci_device_suspend;
drv->driver.remove = pci_device_remove;
/* register with core */
driver_register(&drv->driver);
이상적으로 bus는 해당 field가 아직 설정되지 않았을 때만 초기화해야 합니다. 그러면 개별 driver가 자신의 generic method를 구현해 override할 수 있습니다.
점진적 이식을 가능하게 하는 adapter 호출 흐름입니다.
Step 5: generic driver binding 지원
328-376model은 device나 driver가 언제든 bus에 동적으로 등록될 수 있다고 가정합니다. 등록 시 device를 driver에 bind하거나, driver를 자신이 지원하는 모든 device에 bind해야 합니다.
driver는 보통 지원하는 device ID 목록을 가지고 bus driver는 이를 등록 device의 ID와 비교합니다. ID format과 비교 semantics는 bus-specific이므로 generic model은 이를 일반화하려 하지 않습니다.
대신 bus가 `struct bus_type`에 비교 method를 제공할 수 있습니다.
int (*match)(struct device * dev, struct device_driver * drv);
`match`는 driver가 device를 지원하면 positive value, 아니면 0을 반환해야 합니다. 주어진 driver의 지원 여부를 아직 결정할 수 없으면 `-EPROBE_DEFER` 같은 error code도 반환할 수 있습니다.
device 등록 시 bus의 driver list를 순회하며 match를 찾을 때까지 각 driver에 `bus->match()`를 호출합니다. driver 등록 시에는 bus의 device list를 순회하며 아직 driver가 차지하지 않은 각 device에 `bus->match()`를 호출합니다.
binding에 성공하면 `device->driver`를 설정하고 device를 per-driver device list에 추가하며, driver의 sysfs directory에 device physical directory를 가리키는 symlink를 만듭니다.
/sys/bus/pci/drivers/
|-- 3c59x
| `-- 00:0b.0 -> ../../../../devices/pci0/00:0b.0
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
| `-- 00:00.0 -> ../../../../devices/pci0/00:00.0
|-- e100
| `-- 00:0c.0 -> ../../../../devices/pci0/00:0c.0
`-- serial
이 driver binding은 bus가 현재 사용하는 기존 driver binding mechanism을 대체해야 합니다.
device와 driver 중 어느 쪽이 나중에 등록되어도 같은 match 절차를 사용합니다.
Step 6: hotplug callback 제공
377-399원문이 작성된 시점의 동작에서는 device가 driver model core에 등록될 때마다 userspace에 알리기 위해 `/sbin/hotplug` program을 호출했습니다. user는 device 삽입 또는 제거 때 수행할 action을 정의할 수 있습니다.
driver model core는 environment variable로 여러 argument를 userspace에 전달합니다.
- `ACTION`: `add` 또는 `remove`로 설정됩니다.
- `DEVPATH`: sysfs에서 device의 physical path로 설정됩니다.
bus driver는 userspace가 사용할 추가 parameter도 제공할 수 있습니다. 그러려면 `struct bus_type`의 `hotplug` method를 구현합니다.
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
이 callback은 `/sbin/hotplug`를 실행하기 직전에 호출됩니다.
Step 7: bus list와 lock 정리
400-440generic bus, device, driver structure는 bus driver가 private하게 정의한 여러 field를 대체할 수 있습니다.
`struct bus_type`은 해당 bus type에 등록된 모든 device의 list를 포함합니다. 여기에는 그 bus type의 모든 instance에 있는 device가 모두 포함되므로, bus 내부 device list를 제거하고 generic list를 사용할 수 있습니다.
core는 이 device에 접근할 iterator를 제공합니다.
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *));
`struct bus_type`은 등록된 모든 driver의 list도 포함합니다. bus driver가 유지하는 내부 driver list도 generic list로 대체할 수 있으며 device와 비슷하게 순회합니다.
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
자세한 내용은 `drivers/base/bus.c`를 참고하십시오.
`struct bus_type`에는 device와 driver list에 대한 모든 core access를 보호하는 `rwsem`도 있습니다. bus driver 내부에서도 이를 사용할 수 있으며, bus가 유지하는 device 또는 driver list에 접근할 때 사용해야 합니다.
core가 제공하는 list·iterator·동기화 primitive로 옮길 수 있는 항목입니다.
Step 7: 중복 device·driver field 제거
441-448`struct device`와 `struct device_driver`의 일부 field는 bus-specific object 표현의 field와 중복됩니다. bus-specific field를 제거하고 generic field를 사용해도 됩니다.
다만 bus-specific field를 참조하는 모든 driver를 수정해야 할 가능성이 큽니다. 원문은 이러한 수정이 모두 한 줄짜리 변경일 것이라고 설명합니다.
요약과 해설
porting.rst:1-448이 문서는 기존 bus driver를 새 Linux driver model로 점진적으로 이식하는 8단계를 설명합니다. bus layer에 generic `bus_type`, `device`, `device_driver`를 embed하고 core에 등록해 sysfs hierarchy를 만들며, callback adapter와 bus-specific `match`로 기존 driver를 유지한 채 dynamic binding으로 전환합니다.
핵심 안전 규칙은 generic object와 bus-specific object 사이 변환을 `container_of` helper로 명시하고, unregister 뒤 즉시 memory를 해제하지 않으며 `release` callback까지 기다리는 것입니다. 마지막에는 core의 device/driver list, iterator와 `rwsem`으로 private infrastructure를 대체합니다.