플랫폼 디바이스는 리눅스 커널이 USB 또는 PCI등과 같은 버스를 통해 동적으로 감지 할 수 없는 SOC(system-on-chip)에 내장된 시스템 장치로 설명할 수 있다.
커널은 플랫폼 디바이스 메커니즘을 제공하므로써 실제로 존재하는 하드웨어에 대해 알 수있다. 이 글에서는 플랫폼 디바이스의 커널 인터페이스에 대해 설명하며 디바이스 트리와의 통합을 위해 필요한 배경 자료가 될 수있다.
Platform drivers
플랫폼 디바이스는 struct platform_device 로 정의되며 <linux/platform_device.h>에서 찾을 수 있다. 이러한 장치는 가상 "플랫폼 버스"에 연결된 것으로 간주된다.
따라서 플랫폼 디바이스의 드라이버는 플랫폼 버스에 등록해야한다. 이 등록은 platform_driver 구조체를 통해 수행된다.
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
최소한 probe() 및 remove() 콜백을 제공해야하며 다른 콜백은 전원 관리와 관련이 있으며 관련이있는 경우 제공해야한다.
드라이버가 제공해야하는 다른 것은 버스 코드가 실제 장치를 드라이버에 바인딩하는 방법이다. 그 목적으로 사용할 수있는 두 가지 메커니즘이 있다. 첫 번째는 id_table 이다. id_table 구조체는 다음과 같다.
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};
ID 테이블이 있으면 플랫폼 버스는 새 플랫폼 디바이스의 드라이버를 찾을 때마다 이를 스캔한다. 장치 이름이 ID 테이블 항목의 이름과 일치하면 장치는 관리를 위해 드라이버에게 제공되며 일치하는 ID 테이블 항목에 대한 포인터도 드라이버에서 사용할 수 있다.
ID 테이블을 제공하지 않을 경우 driver 필드에 드라이버 이름을 제공한다. 예를 들어, i2c-gpio 드라이버는 다음과 같은 플랫폼 디바이스로 설정된다.
static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio",
.owner = THIS_MODULE,
},
.probe = i2c_gpio_probe,
.remove = __devexit_p(i2c_gpio_remove),
};
이 설정을 통해 "i2c-gpio"로 식별되는 모든 장치가 이 드라이버에 바인딩된다.
플랫폼 드라이버는 다음을 통해 반드시 자신을 커널에 알려야한다.
int platform_driver_register(struct platform_driver *driver);
이 호출이 성공하면 드라이버의 probe() 함수가 호출 될 수 있다. 이 함수는 인스턴스화 할 장치를 설명하는 platform_device 포인터를 가져온다.
struct platform_device {
const char *name;
int id;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
/* Others omitted */
};
dev 필드는 필요한 상황 (예 : DMA 매핑 API)에서 사용할 수 있다. 장치가 ID 테이블 항목을 사용하여 일치하면 id_entry 는 일치하는 항목을 가리킨다.
resource 배열은 메모리 매핑 된 I/O 레지스터 및 인터럽트를 포함한 다양한 리소스를 찾는 데 사용할 수 있다. 리소스 배열에서 데이터를 가져 오기위한 여러 가지 도우미 함수가 있는데 다음은 몇가지 함수의 예를 보여준다.
struct resource *platform_get_resource(struct platform_device *pdev,
unsigned int type, unsigned int n);
struct resource *platform_get_resource_byname(struct platform_device *pdev,
unsigned int type, const char *name);
int platform_get_irq(struct platform_device *pdev, unsigned int n);
"n"매개 변수는 index를 나타내며 0은 첫 번째 리소스를 나타낸다. 예를 들어 드라이버는 다음을 통해 두 번째 MMIO 영역을 찾을 수 있다.
r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Platform devices
처음에 언급했듯이 플랫폼 디바이스는 본질적으로 검색 할 수 없으므로 커널에 장치의 존재를 알리는 다른 방법이 있어야한다.
이는 일반적으로 관련 드라이버를 찾는 데 사용되는 정적 platform_device 구조체를 작성하여 수행된다. 예를 들어 간단한 장치는 다음과 같이 설정 될 수 있다.
static struct resource foomatic_resources[] = {
{
.start = 0x10000000,
.end = 0x10001000,
.flags = IORESOURCE_MEM,
.name = "io-memory"
},
{
.start = 20,
.end = 20,
.flags = IORESOURCE_IRQ,
.name = "irq",
}
};
static struct platform_device my_foomatic = {
.name = "foomatic",
.resource = foomatic_resources,
.num_resources = ARRAY_SIZE(foomatic_resources),
};
이 선언은 1 페이지 MMIO 영역이 0x10000000 에서 시작하고 IRQ 20을 사용하는 "foomatic"장치를 설명한다. 장치는 다음을 통해 시스템에 알린다.
int platform_device_register(struct platform_device *pdev);
플랫폼 디바이스와 관련 드라이버가 모두 등록되면 드라이버의 probe() 함수가 호출되고 장치가 인스턴스화된다.
플랫폼 디바이스를 제거하려면 platform_device_unregister() 함수를 사용한다.
Platform data
위의 정보는 간단한 플랫폼 디바이스를 인스턴스화하는 데 적합하지만 사실 많은 장치들이 그보다 복잡하다. 위에서 설명한 간단한 i2c-gpio 드라이버조차도 i2c 클럭으로 사용되는 GPIO 라인 수와 데이터 라인이라는 두 가지 추가 정보가 필요하다.
이 정보를 전달하는 데 사용되는 게 "Platform data" 이다. 간단히 말해서 필요한 특정 정보를 포함하는 자료구조를 정의하고 이를 플랫폼 디바이스의 dev.platform_data 필드에 전달한다.
i2c-gpio 예제에서 platform_data 구성은 다음과 같다.
static struct i2c_gpio_platform_data my_i2c_plat_data = {
.scl_pin = 100,
.sda_pin = 101,
};
static struct platform_device my_gpio_i2c = {
.name = "i2c-gpio",
.id = 0,
.dev = {
.platform_data = &my_i2c_plat_data,
}
};
드라이버의 probe() 함수가 호출되면 platform_data 포인터를 가져 와서 필요한 나머지 정보를 얻는 데 사용할 수 있다.
다음 링크의 글을 번역 및 수정: https://lwn.net/Articles/448499/
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
12 | 임베디드 개발자를 위한 Hex,Bin,Dec 변환기 유틸 | makersweb | 2023.02.27 | 1234 |
11 | Yocto 프로젝트 3.4 릴리스(honister) 이상 버전으로 마이그레이션 시 참고 사항 | makersweb | 2023.03.21 | 1217 |
10 | libopencm3 활용, Cortex-M 펌웨어 개발 | makersweb | 2019.07.14 | 1112 |
9 | Android 기기를 사용하여 Raspberry Pi SD 카드 작성 방법 | makersweb | 2020.08.01 | 1112 |
8 | 임베디드 비대칭 멀티 프로세싱(asymmetric multiprocessing) 시스템 | makersweb | 2019.12.31 | 968 |
7 | mainline 커널 및 etnaviv 를 사용하는 Wandboard(Freescale i.MX6Q)에서 eglfs를 사용 | makersweb | 2019.10.17 | 959 |
6 | Raspberry Pi 와 ATtiny85 간 I²C 통신 | makersweb | 2023.03.18 | 761 |
5 | 로직분석기와 함께 PulseView 를 사용해서 CAN 신호 캡쳐 | makersweb | 2023.03.16 | 740 |
4 | Raspberry Pi에서 I²C 그리고 Bit-bang (비트뱅) | makersweb | 2023.08.27 | 715 |
3 | ATtiny85 개발보드(HW-260) | makersweb | 2023.01.02 | 682 |
2 | Rockchip VOP | makersweb | 2024.04.22 | 252 |
1 | GNU C 레퍼런스 메뉴얼 - 부록 D | makersweb | 2014.02.28 | 5 |