FSTWikiKelp Ldm
Login:
Password:
Join
E D R S I H P RSS
FrontPage|FindPage|TitleIndex|RecentChanges

Linux Driver Model

2005 KELP Open Seminar
-- 김도집 2005-11-01 01:01:10

Contents

1 서론
2 리눅스 디바이스 드라이버 개요
3 디바이스 등록 및 해제
3.1 platform 디바이스 API
3.1.1 struct platform_device
3.1.2 platform_device_register
3.1.3 platform_device_unregister
4 드라이버 등록 및 해제
4.1 device_driver API
4.1.1 struct device_driver
4.1.2 driver_register
4.1.3 driver_unregister
4.1.4 dev_set_drvdata
4.1.5 dev_get_drvdata
5 device vs driver
6 문자 디바이스 드라이버
6.1 어떤 API를 사용할 것인가
6.2 문자 디바이스 드라이버 API
6.2.1 struct file_operations
6.2.2 register_chrdev
6.2.3 unregister_chrdev
6.2.4 struct cdev
6.2.5 cdev_init
6.2.6 cdev_add
6.2.7 cdev_del
6.2.8 register_chrdev_region
6.2.9 alloc_chrdev_region
6.2.10 unregister_chrdev_region
7 장치 번호
7.1 MKDEV
7.2 MAJOR
7.3 MINOR
8 디바이스 드라이버의 객체 개념
8.1 subsystem, kset, ktype, kobj의 관계
9 kref
9.1 kref API
9.1.1 struct kref
9.1.2 kref_init
9.1.3 kref_get
9.1.4 kref_put
10 kobject
10.1 kobject API
10.1.1 kobject_set_name
10.1.2 kobject_name
10.1.3 kobject_init
10.1.4 kobject_cleanup
10.1.5 kobject_add
10.1.6 kobject_del
10.1.7 kobject_register
10.1.8 kobject_unregister
10.1.9 kobject_get
10.1.10 kobject_put
10.1.11 kobject_get_path
11 kset
11.1 kset API
11.1.1 struct kset
11.1.2 kset_init
11.1.3 kset_add
11.1.4 kset_register
11.1.5 kset_unregister
11.1.6 to_kset
11.1.7 kset_get
11.1.8 kset_put
12 ktype
13 sysfs
13.1 /sys
13.2 sysfs API
13.2.1 sysfs_create_dir
13.2.2 sysfs_remove_dir
13.2.3 sysfs_rename_dir
13.2.4 sysfs_create_file
13.2.5 sysfs_update_file
13.2.6 sysfs_remove_file
13.2.7 sysfs_create_link
13.2.8 sysfs_remove_link
13.2.9 sysfs_create_bin_file
13.2.10 sysfs_remove_bin_file
13.2.11 struct attribute_group
13.2.12 sysfs_create_group
13.2.13 sysfs_remove_group
13.3 사용자를 위한
14 subsystem
14.1 subsystem 개요
14.2 subsystem과 kset과의 관계
14.3 subsystem on sysfs
14.4 subsystem API
14.4.1 decl_subsys
14.4.2 subsystem_init
14.4.3 subsystem_register
14.4.4 subsystem_unregister
14.4.5 subsystem_get
14.4.6 subsystem_put
15 kobj_map
15.1 kobj_map API
15.1.1 kobj_probe_t
15.1.2 struct kboj_map
15.1.3 kobj_map
15.1.4 kobj_unmap
15.1.5 kobj_lookup
15.1.6 kobj_map_init
16 bus
16.1 bus on sysfs
16.2 bus API
16.2.1 bus_register
16.2.2 bus_unregister
16.2.3 bus_for_each_dev
16.2.4 bus_for_each_drv
16.2.5 BUS_ATTR
16.2.6 bus_create_file
16.2.7 bus_remove_file
17 device
17.1 device on sysfs
17.2 device API
17.2.1 struct device
17.2.2 device_register
17.2.3 device_unregister
17.2.4 DEVICE_ATTR
17.2.5 device_create_file
17.2.6 device_remove_file
18 driver
18.1 driver API
18.1.1 driver_register
18.1.2 driver_unregister
18.1.3 DRIVER_ATTR
18.1.4 driver_create_file
18.1.5 driver_remove_file
19 class
19.1 class on sysfs
19.2 class
19.2.1 struct class
19.2.2 class_simple_create
19.2.3 class_simple_destroy
19.2.4 class_simple_device_add
19.2.5 class_simple_device_remove
19.2.6 class_create
19.2.7 class_destroy
19.2.8 class_device_create
19.2.9 class_device_destroy
19.2.10 class_register
19.2.11 class_unregister
19.2.12 class_get
19.2.13 class_put
19.2.14 struct class_attribute
19.2.15 CLASS_ATTR
19.2.16 class_create_file
19.2.17 class_remove_file
19.2.18 struct class_device
19.2.19 class_get_devdata
19.2.20 class_set_devdata
19.2.21 class_devce_register
19.2.22 class_device_unregister
19.2.23 class_device_initialize
19.2.24 class_device_add
19.2.25 class_device_del
19.2.26 class_device_rename
19.2.27 class_device_get
19.2.28 class_device_put
19.2.29 struct classs_device_attribute
19.2.30 CLASS_DEVICE_ATTR
19.2.31 class_device_create_file
19.2.32 class_device_remove_file
19.2.33 class_device_create_bin_file
19.2.34 class_device_remove_bin_file
19.2.35 struct class_interface
19.2.36 class_interface_register
19.2.37 class_interface_unregister
19.3 class의 사용 예
19.3.1 클래스 생성
19.3.2 클래스 디바이스 생성/제거
19.3.3 클래스 제거
20 hotplug
21 udev
22 firmware
22.1 firmware API
22.1.1 struct fw
22.1.2 request_firmware
22.1.3 request_firmware_nowait
22.1.4 release_firmware
22.2 firmware의 사용
23 References

1 서론 #

본 내용은 리눅스 커널 내에서 자주 사용되는 API를 중심으로 열거식으로 정리한 것이다. 유기적인 기능들에 대해서는 설명하지 않으므로 이러한 것을 원한다면 기존의 서적들이나 문서들을 참고하기 바란다.

또한 본 내용은 직접 사용하거나 이럴 거 같다는 유추 식으로 작성된 내용도 있으므로 이것이 정확하다고 말할 수는 없다. 그러나 나름대로는 오류가 없도록 할 것이며 잘못된 내용이 있다면 최대한 빨리 수정할 수 있도록 할 것이다.

커널 버전은 2.6.11 또는 그 이후 버전들을 기준으로 작성하였다. 커널의 API는 고정된 것이 아니라 버전별로 조금씩 변경될 수도 있다. 커널 API에 대한 최신의 정보는 [http]http://lwn.net에서 확인할 수 있다.

- 2005.9.28 김도집 -

2 리눅스 디바이스 드라이버 개요 #

OS의 커널은 모놀리딕 Monolithic과 마이크로 micro 커널로 나눈다. 모놀리딕 커널은 커널이 하나의 커다란 덩어리로 구성된 것이고 마이크로 커널은 여러 개의 독립된 서버 형태로 구성된 것이다. 말하자면 전자는 처음부터 완성된 완구형 장난감이고, 후자는 레고와 같은 블록 조립식 장난감과 비슷한 개념이다.

서로간엔 장단점이 있기에 어느 것이 더 좋다고 할 수는 없다.

짐작하겠지만, 리눅스는 모놀리딕 커널이다. 그렇지만 재미있는 것이 일부 기능을 모듈이라는 동적 적재 가능한 형태로 확장이 가능하다는 것이다. 일반적으로 드라이버가 모듈형태로 사용되지만, 꼭 드라이버가 아니라 하더라고 모듈 형태로 일부 기능을 제공할 수 있다.

모듈 개념이 적용된 것은 커널 버전 1.2대에서 적용되었고 현재까지 그 개념은 이어지고 있다.

커널 2.5대에 접어 들면서 또 하나의 커다란 변화가 있었는데, 바로 kboject라는 드라이버 객체 개념이 추가되었다. 드라이버가 다양해 지고 복잡해 지면서 상호간의 연관성이 중요하게 되었다. 이에 따라 드라이버를 계층화 시켜 관리할 필요성이 점점 제기되었고, 이런 요구를 일관성 있고 체계 있게 구현/관리 하기 위하여 적용되었다고 할 수 있다.

이후 다룰 내용은 이들 객체에 대한 것이다. 장황한 설명보다는 직접 자료형과 API를 보면서 이해하는 것이 더 수월할 것이다. 그럼 리눅스 드라이버 모델에 빠져 봅시다~~~ ^^

3 디바이스 등록 및 해제 #

리눅스 커널 2.6에서는 kobject라는 객체 기반으로 디바이스 및 드라이버의 계층적 구조를 갖고 있다. 예를 들어 등록된 버스가 있고 버스에 새로운 디바이스가 장착되면 버스 드라이버가 디바이스를 탐지하고 이에 맞는 드라이버를 찾아 등록하게 된다.

따라서 버스 상의 디바이스와 드라이버는 커널 내부에서 기본적으로 이름으로 찾게 된다. 따라서 버스 상에 등록된 디바이스의 이름과 드라이버의 이름이 서로 다르다면 정상적으로 드라이버가 동작하지 않을 수 있다.

다음은 여러 버스 형태 중 하나인 platform_bus_type 라는 가상의 플랫폼 버스 상에 새로운 디바이스를 등록하는 방법이다.

3.1 platform 디바이스 API #

3.1.1 struct platform_device #

platform_bus_type 라는 가상의 플랫폼 버스 상의 디바이스를 정의하는 구조체는 <linux/device.h>에 정의되어 있다. 그 구조체는 platform_device 이다.

구조체의 원형은 다음과 같다:
struct platform_device {
  char *name;
  u32 id;
  struct device dev;
  u32 num_resources;
  struct resource *resource;
};

필드설명
name플랫폼 디바이스 이름
id여러개의 플랫폼 디바이스가 있을 경우 id를 통해 구분한다.
dev
num_resources
resource

다음은 struct device;에 대한 내용이다. 이는 에 선언되어 있다.
struct device {
  ...
  struct device *parent;

  struct kobject kboj;
  char bus_id[BUS_ID_SIZE];
  ...

  struct bus_type *bus;
  strcut device_driver *driver;
  ...

  void (*release)(struct device *dev);
};

간단한 사용 예는 다음과 같다:
static void xxx_relese_dev(struct device *dev)
{
}

static struct platform_device xxx_device = {
  .name =           "xxx",
  .id   =           0,
  .dev  = {
     .release = xxx_release_dev,
  },
  .num_resources = 0,
};

3.1.2 platform_device_register #

플랫폼 디바이스를 등록한다.

함수 원형은 다음과 같다:
int platform_device_register(struct platform_device *pdev)

3.1.3 platform_device_unregister #

플랫폼 디바이스의 등록을 해제한다.

함수 원형은 다음과 같다:
void platform_device_unregister(struct platform_device *pdev)

4 드라이버 등록 및 해제 #

4.1 device_driver API #

4.1.1 struct device_driver #

driver_register/device_unregister 함수에서 사용하는 자료형은 <linux/device.h>에 선언되어 있다.
struct device_driver {
  const char *name;
  struct bus_type *bus;

  struct completion unloaded;
  struct kobject kobj;
  struct klist klist_devices;
  struct klist_node knode_bus;

  struct module *owner;

  int (*probe) (struct device *dev);
  int (*remove) (struct device *dev);
  int (*shutdown) (struct device *dev);
  int (*suspend) (struct device *dev, pm_message_t state, u32 level);
  int (*resume) (struct device *dev, u32 level);
};

필드설명
name디바이스 드라이버의 이름을 문자열 형으로 지정한다 (필수)
bus보통은 &platform_bus_type으로 지정한다 (필수)
unloaded
kobj내부적으로 드라이버 객체를 관리하기 위한 것으로 직접적으로 사용하지 않는다 (임의 지정하지 않는다)
klist_devices
knode_bus
owner
probe()디바이스의 초기화 루틴이다 (필수)
remove()디바이스가 제거될 때 호출되는 루틴이다 (필수)
shutdown()
suspend()절전모드로 들어갈 때 호출된다 (필수)
resume()절전모드를 빠져나올 때 호출된다 (필수)
/!\ 위 테이블에서 foo()와 같이 ()가 붙은 필드는 함수(포인터)를 의미한다.

4.1.2 driver_register #

버스를 갖는 드라이버를 등록할 때 사용하는 함수이다. 함수의 원형은 다음과 같다:
int driver_register(struct device_driver *drv);

관련함수: driver_unregister

static struct device_driver foo_driver = {
  .name    = "foo",
  .bus     = &platform_bus_type,
  .probe   = foo_probe,
  .remove  = foo_remove,
  .suspend = foo_suspend,
  .resume  = foo_resume,
};

static int __init foo_init(void) {
  int err;

  err = driver_register(&foo_driver);

  return err;
}

4.1.3 driver_unregister #

driver_register()를 통해 등록된 드라이버를 해제할 때 사용한다. 함수의 원형은 다음과 같다:
void driver_unregister(struct device_driver *drv)

관련함수: driver_register

4.1.4 dev_set_drvdata #

사용자 데이터를 device 구조체의 driver_data 필드를 통해 참조할 수 있도록 설정하는 wrapper 함수이다.

함수 원형은 다음과 같다:
void dev_set_drvdata(struct device *dev, void *data)

관련함수: dev_get_drvdata

4.1.5 dev_get_drvdata #

device 구조체의 driver_data 필드의 참조 주소를 가져오는 wrapper 함수이다.

함수 원형은 다음과 같다:
void * dev_get_drvdata(struct device *dev)

관련함수: dev_set_drvdata

5 device vs driver #

커널 2.6에서는 bus 기반 위에서 device를 추가하고 driver를 등록하는 형태를 제공한다. 이러한 방식이 이전 버전에서도 가능했지만, 체계적인 구조를 갖추고 그 기능을 본격적으로 제공하기 시작한 것은 커널 2.6이 그 시작이라고 할 수 있다.

앞서 설명에서는 bus의 한 형태인 platform bus를 소개했다. 이를 이용한 예시를 살펴보자. 다음은 커널 2.6.13의 drivers/video/vesafb.c의 일부 내용이다.
static struct device_driver vesafb_driver = {
  .name = "vesafb",
  .bus = &platform_bus_type,
  .probe = vesafb_probe,
};

static struct platform_device vesafb_device = {
  .name = "vesafb",
};

static int __init vesafb_init(void)
{
  int ret;
  char *option = NULL;

  ...
  ret = driver_register(&vesafb_driver);

  if (!ret) {
    ret = platform_device_register(&vesafb_device);
    if (ret)
      driver_unregister(&vesafb_driver);
    }
  return ret;
}

6 문자 디바이스 드라이버 #

문자 드라이버와 관련된 헤더 파일은 <linux/cdev.h>이다.

리눅스 커널에서 지원하는 대표적인 드라이버의 형태는 문자, 블록, 네트워크 드라이버 등이 있다. 쉽게 접근할 수 있고 가장 많이 사용되는 것이 문자 디바이스 드라이버이다.

디바이스 파일 중 ls -l를 통해 접근권한 내용중 crw-rw---- 등과 같이 c로 시작하는 것이 문자 디바이스 드라이버이다.

커널 버전 2.6으로 넘어오면서 내부적으로 드라이버의 구조는 많은 변화를 겪었다. 그러나 여전히 이전 버전에서 사용하던 API는 그대로 사용 가능하다. 예를 들어 register_chrdev() 등의 문자형 디바이스 드라이버의 등록 함수가 그러하다.

이후에 소개하는 내용은 커널 2.6에서 새롭게 적용된 API를 이용하여 문자형 디바이스 드라이버를 구현하는 방법을 소개하는 것이다. 사실, 커널 버전 2.6.11에서도 이들을 직접 사용하는 예는 그리 많지 않다.

6.1 어떤 API를 사용할 것인가 #

앞서 말한 바와 같이 커널 2.6에서는 cdev로 시작하는 새로운 API를 제공하고 있다. 기존의 register_chrdev/unregister_chrdev는 major 번호를 기준으로 minor 번호가 0에서 255까지 일괄적으로 디바이스 자원을 등록/해제한다.

반면에 새 API를 사용하면 디바이스를 등록/해제를 세밀히 할 수 있다. 디바이스의 시작 번호를 주어 이후 몇개를 등록할지를 결정하여 몇개의 minor 번호를 사용할지를 결정할 수 있다. 이 새 API는 cdev_ 또는 alloc_ 로 시작한다.

6.2 문자 디바이스 드라이버 API #

함수설명
register_chrdev문자 디바이스를 등록한다.
unregistr_chrdev문자 디바이스를 해제한다.
cdev_initcdev를 초기화한다.
cdev_addcdev에 dev를 등록한다.
cdev_delcdev에 dev를 해제한다.
register_chrdev_region디바이스가 사용할 디바이스 번호를 요구한다.
alloc_chrdev_region디바이스가 사용할 디바이스 번호를 동적으로 할당받아 요구한다.
unreigster_chrdev_region요구하여 사용중인 디바이스 번호를 해제한다.

6.2.1 struct file_operations #

디바이스 드라이버의 오퍼페이션을 정의하기 위한 자료형이다. 이는 선택이 아닌 필수적으로 알아야 하는 자료형이다. 이는 에 선언되어 있다.

struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        int (*ioctl) (struct inode *, struct file *, unsigned int,
                      unsigned long);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, struct dentry *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
                          loff_t *);
        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
                           loff_t *);
        ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *,
                             int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long,
                                           unsigned long, unsigned long,
                                           unsigned long);
        int (*check_flags)(int);
        int (*dir_notify)(struct file *filp, unsigned long arg);
        int (*flock) (struct file *, int, struct file_lock *);
};

6.2.2 register_chrdev #

새로운 문자형 디바이스를 등록하는데, 이는 minor 번호가 0에서 시작해서 256개의 디바이스를 할당한다.

int register_chrdev(unsigned int major, const char *name,
                    struct file_operations *fops);

6.2.3 unregister_chrdev #

register_chrdev를 통해 할당 받은 디바이스 자원을 해제한다.

void unregister_chrdev(unsigned int major, const char *name);

6.2.4 struct cdev #

문자 디바이스에서 사용하는 기본 자료형은 struct cdev;이다. 이는 에 선언되어 있다.

struct cdev {
  struct kobject kobj;
  struct module *owner;
  struct file_operations *ops;
  struct list_head list;
  dev_t dev;
  unsigned int count;
};

필드설명
kobjkobject 객체
owner모듈의 소유자로, 내부적으로 모듈의 ID정도로 사용된다. 일반적으로 THIS_MODULE 로 설정
ops파일 오퍼레이션을 지정한다.
list연결 리스트
dev디바이스 번호
count디바이스 개수

/!\ THIS_MODULE 는 매크로로 에 선언되어 있다. 이는 모듈을 컴파일 시에 그 값이 정해지며, 한 시스템 내에서 서로 다른 모듈은 서로 다른 값을 갖게 된다.

6.2.5 cdev_init #

cdev를 초기화 한다.

void cdev_init(struct cdev *cdev, struct file_operations *fops)

cdev를 0으로 모두 초기화 하고, cdev->kobj.ktype를 ktype_cdev_default 값으로 설정하고 cdev->kobj도 kobject_init를 통해 초기화 한다. 이후 cdev->ops를 fops로 설정한다.

위 설명이야 어떻든 사용자에게 중요한 것은 cdev 내의 ops를 사용자가 넘겨준 fops로 설정하는 것이다.

사용 예는 다음과 같다:
static struct file_operations xxx_fops = {
  .read = xxx_read,
  .write = xxx_write,
  .open = xxx_open,
  .release = xxx_release,
  .owner = THIS_MODULE,
};

static struct cdev xxx_cdev = {
  .kobj = {.name = "xxx", },
  .owner = THIS_MODULE,
};

static int __init xxx_init(void)
{
  dev_t dev = MKDEV(XXX_MAJOR, 0);

  if (register_chrdev_region(dev, MAX_XXX_MINORS, "xxx"))
    goto error;

  cdev_init(&xxx_cdev, &xxx_fops);

  ...

6.2.6 cdev_add #

문자 디바이스에 대한 객체의 관리는 cdev_map를 통해 디바이스 번호와 매핑이 된다. 이 함수는 cdev_map에 dev 라는 디바이스 번호를 등록한다.

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

예시:
다음은 drivers/char/raw.craw_init() 함수의 일부 내용이다.
static int __init raw_init(void)
{
  int i;
  dev_t dev = MKDEV(RAW_MAJOR, 0);

  if (register_chrdev_region(dev, MAX_RAW_MINORS, "raw"))
    goto error;

  cdev_init(&raw_cdev, &raw_fops);
  if (cdev_add(&raw_cdev, dev, MAX_RAW_MINORS)) {
    unregister_chrdev_region(dev, MAX_RAW_MINORS);
    goto error;
  }

  ...
  return 0;

error:
  ...
  return 1;
}

6.2.7 cdev_del #

cdev_add를 통해 할당 받은 자원을 반환한다.

void cdev_del(struct cdev *p)

관련함수: cdev_add

예시:
다음은 drivers/char/raw.craw_exit 함수의 일부 내용이다.
static void __exit raw_exit(void)
{
  ...
  cdev_del(&raw_cdev);
  unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS);
}

6.2.8 register_chrdev_region #

cdev_init를 통해 cdev를 초기화 하기 전에 호출된다. 이는 name의 문자 디바이스를 from에 해당하는 major와 minor 번호에서 부터 시작해서 count 개수 만큼의 디바이스를 할당 받는다. 본 함수는 에 선언되어 있다.


경우에 따라서는 디바이스 번호를 동적으로 할당 받고자 할 때가 있을 것이다. 이런 경우엔 reigster_chrdev_region 대신 alloc_chrdev_region을 사용한다.

int register_chrdev_region(dev_t from, unsigned count, const char *name)

/!\ 주의할 점은 count 값에 따라 시작 디바이스의 major 번호와 마지막 디바이스의 major 번호가 서로 다를 수가 있다.

관련함수: alloc_chrdev_region, unregister_chrdev_region

6.2.9 alloc_chrdev_region #

디바이스 번호를 동적으로 할당 받고자 할 때 register_chrdev_region 대신 사용한다. 해제할 때는 디바이스 번호를 알고 있으므로 unregister_chrdev_region을 사용한다.

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
                        const char *name);

dev는 할당 받은 디바이스 번호를 넘겨 받을 포인터이다. name이라는 이름을 갖는 문자형 디바이스를 디바이스 번호의 minor 번호는 baseminor로 시작해서 count 개수 만큼 할당 받는다.

6.2.10 unregister_chrdev_region #

register_crhdev_region의 대응 함수로, from에서 count 개수 만큼 할당 받은 디바이스의 자원을 반환한다. 본 함수는 <linux/fs.h>에 선언되어 있다.

void unregister_chrdev_region(dev_t from, unsigned count)

관련함수: register_chrdev_region

예시:
다음은 drivers/char/raw.craw_exit 함수의 일부 내용이다.
static void __exit raw_exit(void)
{
  ...
  cdev_del(&raw_cdev);
  unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS);
}

7 장치 번호 #

장치 번호는 dev_t 형으로 u32형이다. 상위 20비트가 주번호이며 하위 12비트가 부번호이다.

내부적으로 *chrdevs?MAX_PROBE_HASH가 선언되어 있다. MAX_PROBE_HASH는 255이다. 즉, 주번호가 인덱스가 되어야 하는데, 20비트라면 255개만으로는 주번호를 모두를 포함할 수 없다. 그래서 커널에서는 주번호%MAX_PROBE_HASH 로 나눈 값을 인덱스로 사용하여 그 인덱스를 기준으로 linked list로 연결하게 된다.

7.1 MKDEV #

ma라는 major 번호와 mi라는 minor 번호를 통해 디바이스 번호를 만든다.

dev_t MKDEV(ma, mi)

7.2 MAJOR #

디바이스 번호에서 major 번호를 가져온다.

unsigned int MAJOR(dev_t n)

7.3 MINOR #

디바이스 번호에서 minor 번호를 가져온다.

unsigned int MINOR(dev_t n)

8 디바이스 드라이버의 객체 개념 #

객체라고 하면 흔히 C++이나 JAVA와 같은 객체 지향 프로그래밍 언어를 생각한다. 사실 C에서 객체라는 말은 거의 사용하지 않는다. 그러나 C도 다른 객체 지향 프로그래밍 언어와 같이 객체 지향적으로 프로그래밍을 할 수 있다.

혹자는 C는 객체 지향 언어가 아니고 객체 지향이 아닌 순차적 프로그래밍 언어라고 할지도 모르겠다. 여하튼, 여기서 그런 것을 논하고 싶은 생각은 없다.

C에서 객체를 구현하는 것이 쉬운 것은 아니며, 이해하기도 쉬운 것은 아니다. 그러나 포인터와 struct를 이용하여 C에서도 객체를 구현할 수 있다. 또한 gcc라는 강력한 컴파일러 덕분에 표준이 아니긴 하지만, 확장된 C언어의 기능을 이용하여 다양한 것을 시도할 수 있다.

리눅스에서 다양한 디바이스를 지원하고 점점 더 복잡해지면서, 이전과 같은 열거식의 디바이스 관리는 점점 더 어려워졌다. 특히, USB와 같은 계층적 구조의 디바이스의 경우 단순 열거식 만으로는 몇가지 문제가 야기되었다.

예를 들어, USB 허브에 여러 디바이스가 물려 있다고 하자. 이런 경우 허브도 하나의 USB 디바이스고 그 허브에 물려 있는 것들도 디바이스에 해당한다. 이런 경우 그 중간에 물려 있는 장치인 허브를 호스트에서 제거하게 되면 어떻게 될까? 호스트 입장에서 보면 허브에 물려 있던 디바이스들도 제거된 것이다. 그런데 문제는 여기에 있다. 디바이스를 단순 열거하게 되면, 어떤 디바이스가 방금 제거된 허브에 물려 있던 디바이스인지 알 수가 없다는 것이다. 물론, 허브를 관리하는 디바이스 드라이버가 그 허브에 물린 디바이스에 대한 정보를 갖고 있다고 하면 한 해결 방법이 될 수도 있다. 그러나 이것도 한계가 있다. 허브에 또 다른 허브가 붙고 그 허브에 또 다른 허브가 붙는 식으로 계속 확장된다면 관리해야 하는 정보는 기하 급수적으로 는다. 이러한 문제를 해결하기 위하여 디바이스를 계층적으로 관리할 필요성이 제기 되었고 이를 위해 리눅스 커널 2.5 대에서 kobject라는 객체를 정의하고 이를 기반으로 계층적 디바이스 관리 체계를 정립하였다.

달리 말하면, 개발자에겐 kobject를 이해해야 한다는 과제가 주어진 것이다. 디바이스의 관리 편리성을 위해 제안된 것이 역설적으로 개발자에게 kobject를 이해해야 하는 어려움을 야기시켰다. 사실, 몰라도 개발하는데, 큰 불편함은 없다. 이러한 내용은 하부에 숨겨져 있어 실제 개발에서는 직접 다루는 일은 드물기 때문이다. 그렇지만, 분명 알아야 할 때가 올 것이며, 그 기본을 이해하지 못한다면 개발에 어려움이 있을 수도 있다.

계층적 구조를 갖기 위해서 포인터와 struct의 사용으로 그 구조를 따라가기가 만만치 않은 것이 사실이다. 그러나 핵심은 앞서 말한 것과 같이 디바이스 또는 그와 관련된 정보를 객체로 표현하고 이에 대한 최소 단위가 kobject이고 이를 위해 더 상위 개념들의 객체들이 파생되었다. 또한 kojbect는 refcount라는 참조 카운트를 갖는데, 이는 현재 kobject의 유효성을 알려준다. 예를 들어 refcount가 0이 아닌 값이면 kobject는 유효한 것이며, 그렇지 않고 0이면 더이상 kobject는 유효하지 않음을 의미한다.

덧붙여 hotplug인을 위한 event를 사용자 영역으로 넘겨줌으로써 디바이스의 상태 변화를 알려줘 적절한 조치를 취할 수 있도록 하고 있다. 이는 사용중 착탈식이 가능한 디바이스에 매우 유용한 기능 중 하나이다.

8.1 subsystem, kset, ktype, kobj의 관계 #

앞서 이야기 한 바와 같이 kobject라는 객체가 제안되었고 이에 따라 파생된 객체들이 kset, ktype, subsystem, class 등이다.

앞으로 나올 이야기지만, 간단히 kset, ktype, kobj의 관계에 대해 알아보자.

kset의 자료형은 struct kset;, ktype의 자료형은 struct kobj_type;, kobj의 자료형은 앞에서도 다룬 struct kobject;이다.

이들의 역학 관계를 간단히 살펴보면 다음과 같다:
  kset --- kobj
   |
   +--->ktype+--->release()
   |         |
   |         +--->sysfs_ops
   +--->kset_hotplug_ops

kset은 비슷한 유형(type)의 kobj를 갖는 상위 객체라고 할 수 있다. 유형이라고 하니 ktype과 혼동이 될 수 있는데, 이는 전혀 다른 의미이다. 그 의미는 ktype이 어떤 것을 정의하고 있는지 확인하면 알 수 있다. 바로 release와 sysfs에 대한 처리 함수들이 정의함을 할 수 있다. 즉 이것은 kobject 그 자체에 대한 유형을 처리하기 위한 정의이다. 너무 자세하게 설명하고 있는데, ktype에서 다시 보자. ^^

kset의 또 하나의 중요한 역할 중 하나가 hotplug 기능에 대한 오퍼레이션을 정의하고 있다는 것이다. 이 역시 이후 자세히 보도록 하자.

여기서 설명하고자 하는 것은 kset, ktype, kobj가 어떤 관계인지를 파악하는데 도움을 주고자 한 것이다. 다소 모호하긴 하지만, 그래도 어느정도 이해가 되었으라 믿는다.

다음은 버스에 대한 계층적 구조를 도식화 한 것이다.
ldm_bus_hierarchy2.png

ldm_bus_hierarchy.png

bus_subsys는 decl_subsys()를 통해 선언되어 있다. 이때 bus_subys 객체 내의 필드인 .kset.ktype은 bus_ktype를 포인트 하고 있다. 당연히 bus_subsys의 kobject의 이름은 "bus"로 선언된다.

xxx_bus->subsys내의 .kset.kobj.kset이 bus_subsys 내의 kset을 포인트하도록 한다.

9 kref #

kobject의 중요한 목적 중 하나가 refcount를 갖고 있다는 것이다. 즉, 유효성 여부를 판별하기 위한 것으로 계층적 구조 상에서 빼 놓을 수 없는 것이다.

kobject가 refcount를 직접 관리 하지 않고 kref라는 데이터 형을 정의하여 사용한다.

struct kref {
  atomic_t refcount;
};

위 내용에서 atomic_t라는 자료형이 있다. 이는 원자적 처리를 보장해 주는 자료형이다. 화학에서 원자라 함은 원소로서의 특성을 잃지 않는 범위에서 도달할 수 있는 물진의 기본적인 최소 입자라 한다. 즉 원자적이라 함은 다른 처리가 중간에 끼어들 수 없는 최소 단위의 실행 단위를 의미한다.즉 다른 어떤 방해도 없이 한번에 처리됨을 의미한다.

9.1 kref API #

함수설명
kref_initrefcount 값을 1로 초기화 한다.
kref_getrefcount 값을 1 증가 시킨다.
kref_putrefcount 값을 1 감소 시킨다. 만약, 0이면 release 함수를 실행한다.

9.1.1 struct kref #

struct kref {
  atomic_t refcount;
};

9.1.2 kref_init #

refcont의 값을 1로 초기화 한다.

void kref_init(struct kref *kref);

9.1.3 kref_get #

atomic_inc를 이용하여 refcount 값을 1 증가 시킨다.

void kref_get(struct kref *kref);

9.1.4 kref_put #

atomic_dec_and_test()를 이용하여 refcount의 값을 1 감소 시킨다. 만약, 그 값이 0이면 release()함수를 실행하고 1을 반환한다. 그렇지 않다면 0을 반환한다.

int kref_put(struct kref *kref, void(*release)(struct kref *kref));

10 kobject #

kobject는 struct kobject;를 기본 자료 데이터 구조를 갖는다. 이는 에 정의되어 있다.

kobject가 LDM에서 가장 기본이 되는 자료형이다. 이는 단독으로 사용되지 않는다. 즉, 사용자가 이를 직접 사용해야 할 경우는 없다. 상위 자료형이나 API를 통해 kobject는 추상화된다. LDM이 내부적으로 어떻게 구성되는지를 이해하는데, kobject 자료형을 알면 도움이 된다.

struct kobject {
  char *k_name;
  char name[KOBJ_NAME_LEN];
  struct kref kref;
  struct list_head entry;
  struct kobject *parent;
  struct kset *kset;
  struct kobj_type *ktype;
  struct dentry *dentry;
};
Linux kernel v2.6.11

필드설명
k_namek_name은 포인터로 name을 가리킨다.
name문자열의 이름 필드로 최대 20자를 가질 수 있다.
kref
entry
parent
kset
ktype
dentry

k_name은 kobject의 name을 변경하게 될 때 이전 이름을 비교하기 위하여 사용된다. 만약, k_name이 가리키는 이름과 name의 이름이 같지 않다면 k_name이 가리키는 주소를 해제하고 k_name은 name을 지시하도록 한다.

10.1 kobject API #

함수설명
kobject_set_name
kobject_name
kobject_init
kobject_cleanup
kobject_add
kobject_del
kobject_register
kobject_unregister
kobject_get
kobject_put
kobject_get_path

10.1.1 kobject_set_name #

kobj의 이름을 설정한다. 에 선언되어 있다.

int kobject_set_name(struct kobject *kobj, const char *fmt, ...)

fmt 이하의 인자는 printk와 같은 방식의 인자를 갖는다. 하나의 kobject가 만들어질 때 반드시 이름이 지정되어야 하며, 20자를 넘지 않도록 하는 것이 좋다. 만약, 이름이 20자 이상이 되는 경우엔 kobject의 name 필드를 동적으로 커널 메모리를 할당 받아 사용한다. 그러나 가급적이면, 20자를 넘지 않는 것이 좋을 것이다.

예시:
다음은 {drivers/base/bus.c}}}의 bus_register()의 일부 내용이다.
reval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
if (retval)
  goto out;

10.1.2 kobject_name #

kobj의 k_name를 반환한다.

char * kobject_name(struct kobject *kobj);

10.1.3 kobject_init #

kobject 내의 refcont 값을 1로 설정하고 연결 리스트의 헤드인 entry를 초기화한다. kset_get을 통해 kobject가 속한 kset의 kobj의 refcount 값을 증가 시킨다. 만약, kobject_init에서 넘긴 kobj가 어떤 kset에도 속하지 않다면(kobj->kset이 NULL인 경우) kset에 속한 kobj의 refcount 값을 증가 시키는 것은 무시된다.

void kobject_init(struct kobject *kobj);

10.1.4 kobject_cleanup #

kobject 객체에 대한 자원 할당을 해제한다.

vodi kobject_cleanup(struct kobject *);

10.1.5 kobject_add #

자신이 속한 kset에 kobj을 추가한다. 에 선언되어 있다.

int kobject_add(struct kobject *kobj);

kobj의 parent가 지정되어 있지 않다면 kobj의 kset 내의 kobj를 parent를 지정한다.

kobject_add를 통해 생성된 kobject는 sysfs에서 하나의 디렉토리를 생성하게 된다. 자신의 parent를 기준으로 새로운 디렉토리를 생성한다.

이렇게 하여 계층적 구조를 갖게 된다.

10.1.6 kobject_del #

계층 상에서 객체를 제거한다.

void kobject_del(struct kobject *);

함수 내부적으로는 sysfs_remove_dir를 호출 후 unlink를 호출한다.

10.1.7 kobject_register #

객체를 초기화 하고 추가한다.

int kobject_register(struct kobject *);

kobject_register는 내부적으로 kobject_init과 kobject_add를 호출한다.이후 등록이 성공적이면 kobject_hotplug를 호출하여 새 객체가 ADD 되었음을 알리게 된다.

10.1.8 kobject_unregister #

hotplug를 사용한다면 디바이스가 제거(REMOVE) 됨을 알려준다. 이후 kobject_del를 통해 kobj의 sysfs의 정보를 삭제한다. 이후 kobject_put를 호출하여 kobject의 refcount 값을 하나 감소 시킨다. 이때 refcount가 0이면 ktype의 release를 통해 최종적으로 kobject 객체에 대한 자원을 완전히 해제하게 된다.

void kobject_unregister(struct kobject *);

10.1.9 kobject_get #

객체에 대한 refcount 값을 증가시킨다.

struct kobject *kobject_get(struct kobject *);

10.1.10 kobject_put #

객체에 대한 refcount 값을 감소 시킨다.
void kobject_put(struct kobject *);

이 함수는 내부적으로 kref_put를 호출한다.

10.1.11 kobject_get_path #

객체와 관련된 경로명을 반환받는다.

char *kobject_get_path(struct kobject *kobj, int gfp_mask);

11 kset #

kset은 kobject를 포함하고 있는 상위 객체이다. 다시 말하면 kset은 kobjects를 담고 있는 하나의 그룻 역할을 한다. 새로운 kset 객체가 생성되면 그 kset 내의 kobject 객체가 만들어지고 이에 대응되는 디렉토리가 sysfs에 만들어진다. 반면에 새로운 kobject 객체가 생성되었다고 sysfs에 디렉토리나 파일이 만들어지는 것은 아니다. 하나의 kobject는 어느 kset에는 속해야만 한다. kset은 그 상위 객체인 subsys에 속해야만 한다.

kset의 자료형은 struct kset;이다. 이는 <linux/kobject.h>에 정의되어 있다.

struct kset {
  struct subsystem *subsys;
  struct kobj_type *ktype;
  struct list_head list;
  struct kobject kobj;
  struct kset_hotplug_ops *hotplug_ops;
};

필드설명
subsys
ktype
list
kobj
hotplug_ops

11.1 kset API #

함수설명
kset_init
kset_add
kset_register
kset_unregister
to_kset
kset_get
kset_put

11.1.1 struct kset #

kset의 자료형은 struct kset;이다. 이는 에 정의되어 있다.

struct kset {
  struct subsystem *subsys;
  struct kobj_type *ktype;
  struct list_head list;
  struct kobject kobj;
  struct kset_hotplug_ops *hotplug_ops;
};

필드설명
subsys
ktype
list
kobj
hotplug_ops

11.1.2 kset_init #

kset 객체를 초기화 한다. 초기화는 kset 내의 kobj 필드를 초기화(kobject_init)하고 연결 리스트의 헤드인 list 필드를 초기화한다.

void kset_init(struct kset *k);

11.1.3 kset_add #

새로운 kset 객체를 추가한다.

int kset_add(struct kset *k);

새로운 k 내의 필드 인 kobj에 대한 parent(kobj.parent가 NULL)와 kset(kobj.kset가 NULL)이 존재하지 않고 k가 어느 subsys(k->subsys가 NULL이 아니다)에 속해 있다면, k->kobj의 parent는 subsys 내의 kset.kobj를 가리킨다.

마지막으로 kobject_add를 통해 k->kobj를 해당하는 kset내에 추가한다.

커널 소스에서 kset_add를 직접 호출하기 보다는 새로운 kset이 추가되는 경우는 kset_register를 통하게 된다. 그 외 경우로 subsystem_register에서 kset_add를 호출하기도 한다.


11.1.4 kset_register #

int kset_register(struct kset *k);

11.1.5 kset_unregister #

void kset_unregister(struct kset *k);

11.1.6 to_kset #

struct kset *to_kset(struct kobject *kobj);

11.1.7 kset_get #

struct kset *kset_get(struct kset *k);

11.1.8 kset_put #

void kset_put(struct kset *k);

12 ktype #

struct kobj_type {
  void (*release)(struct kobject *);
  struct sysfs_ops *sysfs_ops;
  struct attribute **default_attrs;
};

13 sysfs #

13.1 /sys #

sysfs는 가상 파일 시스템으로 루트 파일 시스템(/)에 sys라는 이름의 디렉토리에 마운트가 된다. 이는 사용자에게 앞서 말한 객체 또는 그에 대한 정보를 사용자가 이용할 수 있도록 구성된 파일 시스템이다.

일반 파일 시스템과 동일한 방식으로 동작한다. 차이점이라면 임의로 디렉토리 및 파일의 생성/제거 등은 할 수 없다. 그러나 파일의 읽기/쓰기/변경 등은 그 권한에 따라 사용자가 이용할 수 있다.

다음은 /sys 아래에 보여주는 디렉토리에 대한 한 예이다.
/sys
|-- block
|-- bus
|-- class
|-- devices
|-- firmware
|-- kernel
|-- module
`-- power

13.2 sysfs API #


struct sysfs_ops {
  ssize_t (*show)(struct kobject *, struct atrribute *, char *);
  ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

함수설명
sysfs_create_dir디렉토리를 만든다. 직접 호출하여 사용하지 않는다.
sysfs_remove_dir디렉토리를 삭제한다. 직접 호출하여 사용하지 않는다.
sysfs_rename_dir디렉토리 명을 변경한다. 직접 호출하여 사용하지 않는다.
sysfs_create_file
sysfs_update_file
sysfs_remove_file
sysfs_create_link
sysfs_remove_link
sysfs_create_bin_file
sysfs_remove_bin_file
sysfs_create_group
sysfs_remove_group

13.2.1 sysfs_create_dir #

객체를 위한 디렉토리를 생성한다. 함수는 <linux/sysfs.h>에 선언되어 있다.

int sysfs_create_dir(struct kobject *kobj);

사용자가 직접 이를 호출하여 사용되지는 않는다. 이는 /lib/kobject.c 정의된 static int creat_dir() 내에서 호출되는 식으로 불려진다.

13.2.2 sysfs_remove_dir #

객체를 위한 디렉토리를 삭제한다.

void sysfs_remove_dir(struct kobject *kobj);

이 역시 사용자가 직접 호출하여 사용되지는 않는다. kobject_del을 호출하면 sysfs_remove_dir이 호출된다.

13.2.3 sysfs_rename_dir #

객체에 대한 디렉토리 이름을 변경한다.

int sysfs_rename_dir(struct kobject *kobj, const char *new_name);

그러나 이 함수를 직접 호출하여 사용하는 것은 바람직하지 않으며, kobject_rename를 사용토록 한다. kobject_rename 함수가 sysfs_rename을 호출한다.

13.2.4 sysfs_create_file #

sysfs에 파일을 생성한다.

int sysfs_create_file(struct kobject *, const struct attribute *);

예시:
다음은 drivers/base/driver.c의 driver_create_file()상에 사용된 예이다.
int driver_create_file(struct device_driver *drv, struct driver_attribute *attr)
{
  int error;
  if (get_driver(drv)) {
    error = sysfs_create_file(&drv->kobj, &attr->attr);
      put_driver(drv);
    } else
      error = -EINVAL;
    return error;
}

13.2.5 sysfs_update_file #

객체의 attribute 파일의 timestamp를 갱신한다.

int sysfs_update_file(struct kobject *kobj, const struct attribute *attr);

파일 내용상의 어떤 변화를 주기 위한 것이 아니다. 사실, 이 함수는 hotplug를 사용하는 디바이스에서 유용할 수 있다. attribute에 어떤 정보를 변경했는데, 이에 해당하는 attribute의 파일 timestamp를 갱신함으로써 파일이 변경되었음을 확인 할 수 있도록 하는 것이다.

예를 들어 pci 디바이스에 어떤 새로운 정보가 갱신되었다면 해당 attribute 파일의 timestamp를 갱신할 수 있다. 이를 통해 사용자는 그 파일의 timestamp를 보고 어떤 변화가 있음을 감지할 수 있을 것이다.

13.2.6 sysfs_remove_file #

sysfs에 생성된 파일을 삭제한다.

void sysfs_remove_file(struct kobject *, const struct attribute *);

예시:
다음은 drivers/base/driver.c의 driver_remove_file()상에서 사용된 예이다.
void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr)
{
  if(get_driver(drv)) {
    sysfs_remove_file(&drv->kobj, &attr->attr);
    put_driver(drv);
  }
}

13.2.7 sysfs_create_link #

심볼릭 링크를 생성한다.

int sysfs_create_link(struct kobject *kobj, struct kobject *target,
                      const char *name);

kobj는 name의 심볼릭 링크가 만들어질 객체이다. target는 심볼릭 링크가 가리킬 객체이다. 심볼릭 링크는 동일한 의미의 객체가 서로 계층 상에 존재할 때 이를 서로 다른 객체로 새로 생성하는 것보다는 심볼릭 링크를 통해 생성하는 것이 더 효율적일 것이다. 이를 위한 함수가 바로 sysfs_create_link이다.

예시:
다음은 리눅스 커널 2.6.11의 drivers/base/bus.c의 bus_add_device() 함수 내용 중 일부 이다.
int bus_add_device(struct device *dev)
{
  struct bus_type *bus = get_bus(dev->bus);
  int error = 0;

  if (bus) {
    ...
    sysfs_create_link(&bus->device.kobj, &dev->kobj, dev->bus_id);
  }
  return error;
}

위 sysfs_create_link에서 보면 /sys/bus/xxx/devices/ 아래 내용이 /sys/devices/ 아래 내용으로 bus_id라는 이름으로 심볼릭 링크됨을 확인 할 수 있다.

실제 /sys 상에서 나타는 결과를 보면 다음과 같다.
greendrm@devil:/sys/bus/pci/devices$ tree /sys/bus/pci/devices/
/sys/bus/pci/devices/
|-- 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
|-- 0000:00:01.0 -> ../../../devices/pci0000:00/0000:00:01.0
|-- 0000:01:00.0 -> ../../../devices/pci0000:00/0000:00:01.0/0000:01:00.0
|-- 0000:03:03.0 -> ../../../devices/pci0000:00/0000:00:1e.0/0000:03:03.0
`-- 0000:03:06.0 -> ../../../devices/pci0000:00/0000:00:1e.0/0000:03:06.0

13.2.8 sysfs_remove_link #

객체 디렉토리 안에 있는 심볼릭 링크를 삭제한다.

void sysfs_remove_link(struct kobject *kobj, char *name);

13.2.9 sysfs_create_bin_file #

객체를 위한 바이너리 파일을 만든다.

int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);

sysfs_create_file과 비슷하지만, 생성되는 파일이 사람이 읽기 쉬운 text 기반의 파일이 아니라 바이너리 파일을 위한 것만 틀리다.

13.2.10 sysfs_remove_bin_file #

객체를 위한 바이너리 파일을 삭제한다.

int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);

13.2.11 struct attribute_group #

여러개의 attribute 파일을 하나의 그룹으로 묶에 서브 디렉토리를 만들고 그 아래에 일괄적으로 만들 수 있다. 이를 위해 리눅스 커널에서는 attribute_group이라는 자료형을 정의하고 관련 API를 제공한다.

이는 에 정의되어 있다.

struct attribute_group {
  const char *name;
  struct attribute **attrs;
};

13.2.12 sysfs_create_group #

객체 아래에 서브 디렉토리를 만들고 attribute 파일들을 생성한다.

int sysfs_create_group(struct kobject *, const struct atrribute_group *);

예시:
다음은 drivers/base/power/sysfs.c 소스에 있는 내용 중 일부 이다.
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
{
  ...
}

static ssize_t state_store(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t n)
{
  ...
}

static DEVICE_ATTR(state, 0644, state_show, state_store);

static struct attribute *power_attrs[] = {
  &dev_attr_state.attr,
  NULL,
};

static struct attribute_group pm_attr_group = {
  .name = "power",
  .attrs = power_attrs,
};

int dpm_sysfs_add(struct device *dev)
{
  return sysfs_create_group(&dev->kobj, &pm_attr_group);
}

void dpm_sysfs_remove(struct device *dev)
{
  sysfs_remove_group(&dev->kboj, &pm_attr_group);
}

13.2.13 sysfs_remove_group #

그룹으로 생성된 attribute 관련 파일 및 디렉토리를 삭제한다.

void sysfs_remove_group(struct kobject *, const struct attribute_group *);

예시:
다음은 drivers/base/power/sysfs.c 소스에 있는 내용 중 일부 이다.
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
{
  ...
}

static ssize_t state_store(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t n)
{
  ...
}

static DEVICE_ATTR(state, 0644, state_show, state_store);

static struct attribute *power_attrs[] = {
  &dev_attr_state.attr,
  NULL,
};

static struct attribute_group pm_attr_group = {
  .name = "power",
  .attrs = power_attrs,
};

int dpm_sysfs_add(struct device *dev)
{
  return sysfs_create_group(&dev->kobj, &pm_attr_group);
}

void dpm_sysfs_remove(struct device *dev)
{
  sysfs_remove_group(&dev->kboj, &pm_attr_group);
}

13.3 사용자를 위한 #

위 사이트에서 libsysfs 라는 sysfs에 대한 common API를 제공하는 라이브러리를 구할 수 있다. 이를 통해 복잡한 sysfs에서 정보를 조금은 수월하게 취할 수 있다.

14 subsystem #

14.1 subsystem 개요 #

디바이스의 계층적 구조에서 가장 상위에 있는 객체라고 할 수 있다. 일반적으로 sysfs에서 가장 상위 디렉토리에 존재하지만, 항상 그런 것은 아니다. 예를 들어, block, bus, class, devices 등은 sysfs의 상위 디렉토리에 존재하지만, acpi와 같은 경우엔 bus/acpi 에 존재한다.

우선, subsystem의 기본 자료형은 struct subsystem;을 보자. 이것은 에 정의되어 있다.
struct subsystem {
  struct kset kset;
  struct rw_semaphore rwsem;
};

subsystem은 kset보다 더 포괄적인 개념이다. subsystem이 새롭게 어떤 기능을 제공하지는 않다. 단지, 내부적으로 kset 연결 리스트 탐색을 직렬화 serialization 한다. 이를 위해 rw_semaphore를 제공한다. 특별한 기능을 제공하진 않지만, kset은 반드시 하나의 subsystem에 속해야만 한다. 따라서 kset 객체 내의 subsys를 통해 같은 subsystem를 찾을 수 있다. 하지만 반대로 subsystem에서 자신에 속한 kset들을 직접 탐색할 수는 없다.

14.2 subsystem과 kset과의 관계 #

앞서 설명한 내용을 그림을 보면 다음과 같다.
           subsystem 
            /  |   \
           /   |    \
       kset  kset  kset

kset들은 자신이 갖고 있는 subsys 필드를 통해 subsystem을 찾을 수 있지만, subsystem은 자신에 속한 kset을 직접 탐색할 수는 없다.

sturct subsystem {
  struct kset kset;
  ...
};

struct kset {
  struct subsystem *subsys;
  ...
  struct list_head list;
  ...
};

14.3 subsystem on sysfs #

앞서 말했듯이 subsystem은 일반적으로 sysfs의 상위 디렉토리인 /sys/ 아래에 존재하게 된다. 그러나 일부는 sysfs의 상위 디렉토리에 존재하지 않는 경우도 있다.

/sys
|-- block
|-- bus
|-- class
|-- devices
|-- firmware
|-- kernel
|-- module
`-- power

이외에도 소스 상에서 찾을 수 있는 subsystem으로는 system_subsys, block_subsys, bus_subsys, class_subsys, devices_subsys, firmware_subsys, class_obj_subsys, acpi_subsys, edd_subsys, vars_subsys, efi_subsys, cdev_subsys, module_subsys, power_subsys, pci_hotplgu_slocks_subsys 등이 있다.

14.4 subsystem API #

함수설명
decl_subsys
subsystem_init
subsystem_register
subsystem_unregister
subsystem_get
subsystem_put

14.4.1 decl_subsys #

새로운 subsystem을 정의한다. 에 선언되어 있다.

delc_subsys(name, type, hotplug_ops);

인자설명
namesubsystem의 이름으로 name_subsys라는 subsystem 객체가 만들어진다.
type필드내의 ktype을 설정한다. NULL로 생략가능하다.
hotplug_opshotplug event 처리를 하고자 할 때 hotplus_ops를 등록한다. NULL로 생략가능하다

/!\ name 인자는 kobject의 name으로도 사용되므로 kobject 최대 이름 길이를 넘지 않도록 한다.

예시:
drivers/block/genhd.c에서 block subsystem을 선언하는 것을 간략히 살펴 보자.
...

 1: static struct kobj_type ktype_block = {
 2:   .release = disk_release,
 3:   .sysfs_ops = &disk_sysfs_ops,
 4:   .default_attrs = default_attrs,
 5: };

...

 6: static struct kset_hotplug_ops block_hotplug_ops = {
 7:   .filter = block_hotplug_filter,
 8:   .hotplug = block_hotplug,
 9: };

/* declare block subsys */
10: static decl_subsys(block, &ktype_block, &block_hotplug_ops);

10번째 줄에서 block_sussys라는 block subsystem 객체를 생성하였다. block의 kset에 대한 기본 ktype을 ktype_block으로 지정하고 hotplug event를 처리하기 위하여 block_hotplug_ops를 등록했다. 만약, ktype이나 hotplug 기능을 block이라는 전제 시스템에 기본적으로 제공하지 않고 특정 kset마다 별도로 설정하고자 한다면 이 두 값을 NULL로 선언하여 생략해도 무방하다.

14.4.2 subsystem_init #

subsys의 세마포어 rwsem과 kset을 초기화(kset_init()을 호출) 한다.

에 선언되어 있다.

void subsystem_init(struct subsystem *subsys);

직접 호출하여 사용되는 예는 없다. 일반적으로 susbsystem_register() 내에서 이를 호출하여 처리하기 때문이다. 그냥 이런 것이 있다는 정도만 알아두자.

14.4.3 subsystem_register #

새로운 subsystem을 등록한다. 에 선언되어 있다.

int subsystem_register(struct subsystem *s);

새로운 subsystem을 등록하면, kset_add를 호출하여 s 내의 kset을 추가한다. 정상적으로 rwsem을 사용하기 위하여 필드 내의 kset의 subsys가 자기 자신을 가리키도록 해야 한다.

14.4.4 subsystem_unregister #

subsystem 객체에 대한 자원 할당을 해제한다.

void subsystem_unregister(struct subsystem *subsys);

실질적으로 subsystem_unregister는 kset_unregister를 호출하고 kset_unregister는 kobject_register를 호출하게 된다. 실질적인 처리는 kobject_register에서 하게 되는 셈이다.


14.4.5 subsystem_get #

struct subsystem *subsys_get(struct subsystem *subsys);

14.4.6 subsystem_put #

void subsys_put(struct subsystem *subsys);

15 kobj_map #

디바이스 번호를 관리하기 위한 것이다. 이와 관련된 API는 헤더 파일에 정의되어 있다.

15.1 kobj_map API #

함수설명
kobj_mapmap에 dev를 추가한다.
kobj_unmapmap에서 dev를 삭제한다.
kobj_lookup해당 domain의 map에서 dev를 찾는다.
kobj_map_initmap을 초기화 한다.

15.1.1 kobj_probe_t #

typedef struct kobject *kobj_probe_t(dev_t, int *, void *);

15.1.2 struct kboj_map #

kobj_map의 기본 자료형은 struct kobj_map이다. 이 자료형은 외부에 공개되지 않는다. 그래서 이 구조체는 에 정의되어 있다.

struct kobj_map {
  struct probe {
    struct probe *next;
    dev_t dev;
    unsigned long range;
    struct module *owner;
    kobj_probe_t *get;
    int (*lock)(dev_t, void *);
    void *data;
  }probe[255];
  struct semaphore *sem;
};

15.1.3 kobj_map #

map에 주어진 데이터를 통해 새 항목(dev)을 추가한다.

int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
             struct module *, kobj_probe_t *probe,
             int (*lock)(dev_t dev, void *), void *data);

예시:[[BR] fs/char_dev.c
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
  ...
  return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

void blk_register_region(dev_t dev, unsigned long range, struct module *module,
                         struct kobject *(*probe)(dev_t, int *, void *),
                         int (*lock)(dev_t, void *), void *data)
{
  kobj_map(bdev_map, dev, range, module, probe, lock, data);
}

15.1.4 kobj_unmap #

void kboj_unmap(struct kobj_map *, dev_t, unsigned long);

15.1.5 kobj_lookup #

두개의 domain (cdev_map, bdev_map) 내에서 dev에 해당하는 디바이스를 찾는다. 그 반환값은 디바이스에 해당하는 kobject를 반환한다.
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index);

예시:
다음은 fs/char/dev.c에서 사용된 예이다.
int chrdev_open(struct inode *inode, struct file *filp)
{
  ...
  if (!p) {
    ...
    kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
    ...
  }
  ...
}

다음은 drivers/block/genhd.c에서 사용된 예이다.
struct gendisk *get_gendisk(dev_t dev, int *part)
{
  struct kobject *kobj = kobj_lookup(bdev_map, dev, part);
  return kboj ? to_disk(kobj) : NULL;
}

15.1.6 kobj_map_init #

map을 초기화한다.

struct kobj_map *kobj_map_init(kobj_probe_t *, struct semaphore *);

예시:
fs/char_dev.c
void __init chrdev_init(void)
{
  cdev_map = kobj_map_init(base_probe, &chrdev_lock);
}

drivers/block/genhd.c
static int __init genhd_device_init(void)
{
  bdev_map = kobj_map_init(base_probe, &block_subsys_sem);
  ...
}

16 bus #

16.1 bus on sysfs #

/sys/bus
|-- i2c
|-- ide
|-- ieee1394
|-- pci
|-- platform
|-- pnp
|-- scsi
|-- serio
`-- usb

16.2 bus API #

함수설명
bus_register
bus_unregister
bus_for_each_dev
bus_for_each_drv
BUS_ATTR
bus_create_file
bus_remove_file

16.2.1 bus_register #

int bus_register(struct bus_type *bus);

16.2.2 bus_unregister #

void bus_unregister(struct bus_type *bus);

16.2.3 bus_for_each_dev #

int buf_for_each_dev(struct bus_type *bus, struct device *start, void *data,
                     int (*fn)(struct device *, void *));

16.2.4 bus_for_each_drv #

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
                     void *data, int (*fn)(struct device_driver *, void *));

16.2.5 BUS_ATTR #

BUS_ATTR(name, mode, show, store);

16.2.6 bus_create_file #

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);

16.2.7 bus_remove_file #

void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);

17 device #


17.1 device on sysfs #

/sys/devices
|-- pci0000:00
|-- platform
|-- pnp0
`-- system

17.2 device API #

함수설명
device_register
device_unregister
DEVICE_ATTR
device_create_file
device_remove_file

17.2.1 struct device #

device의 기본 데이형은 struct device;이다. 이는 에 선언되어 있다.

struct device {
  ...
  struct device *parent;

  struct kobject kobj;
  char bus_id[BUS_ID_SIZE];
  ...

  struct bus_type *bus;
  struct device_driver *driver;

  void *driver_data;
  void *platform_data;

  void *firmware_data;

  ...

  void (*release)(struct device *dev);
};

17.2.2 device_register #

int device_register(struct device *dev);

17.2.3 device_unregister #

void device_unregister(struct device *dev);

17.2.4 DEVICE_ATTR #

DEVICE_ATTR(name, mode, show, store);

17.2.5 device_create_file #

int device_create_file(struct device *dev, struct device_attribute *attr);

17.2.6 device_remove_file #

void device_remove_file(struct device *dev, struct device_attribute *attr);

18 driver #

18.1 driver API #

함수설명
driver_register
driver_unregister
DRIVER_ATTR
driver_create_file
driver_remove_file

18.1.1 driver_register #

int driver_register(struct device_driver *drv);

18.1.2 driver_unregister #

void driver_unregister(struct device_driver *drv);

18.1.3 DRIVER_ATTR #

이 매크로에 대한 정의는 에서 하고 있다. driver_attr_##name 형태의 attribute 자료형이 만들어 진다.

DRIVER_ATTR(name, mode, show, store);

18.1.4 driver_create_file #

드라이버를 위한 sysfs 파일을 만든다.

int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);

18.1.5 driver_remove_file #

드라이버를 위한 sysfs 파일을 삭제한다.

void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);

19 class #

앞서 말한 kobject의 최상위 객체에 해당하는 것이 subsystem이라고 했다. 새로운 타입의 디바이스가 추가되어야 하는데, 새로운 subsystem을 생성하는 것은 권장하지 않는다. 대신에 class라는 객체를 정의해서 새로운 디바이스 타입에 대한 속성을 정의하도록 하고 있다.

기존의 subsystem, kset 등은 디바이스에 종속적으로 결정되는 경향이 있다. 같은 디바이스에서 파생된 속성이라면 그 최상위 객체는 동일할 수 있다. 그러나 class라는 개념은 디바이스와는 상관없이 그 속성에 따라 결정이 된다.

예를 들어 input이라는 입력 시스템에 대해서는 어느 디바이스에 속하든, 같은 class를 갖을 수 있다는 의미이다.

19.1 class on sysfs #

/sys/class
|-- graphics
|-- i2c-adapter
|-- ieee1394
|-- ieee1394_host
|-- ieee1394_node
|-- ieee1394_protocol
|-- input
|-- mem
|-- misc
|-- net
|-- pci_bus
|-- printer
|-- scsi_device
|-- scsi_generic
|-- scsi_host
|-- sound
|-- tty
|-- usb
|-- usb_host
`-- vc

19.2 class #

리눅스 커널 2.6.13(미포함) 이전
  • class_simple_create
  • class_simple_destroy
  • class_simple_device_add
  • class_simple_device_remove

리눅스 커널 2.6.13(포함) 이후
  • class_create
  • class_destroy
  • class_device_create
  • class_device_destroy

19.2.1 struct class #

class의 기본 자료형은 struct class;이다. 이는 에 정의되어 있다.

struct class {
  const char *name;
  struct module *owner;

  struct subsystem subsys;
  struct list_head children;
  struct list_head interfaces;
  struct semaphore sem;

  struct class_attribute *class_attrs;
  struct class_device_attribute *class_dev_attrs;

  int (*hotplug)(struct class_device *dev, char **envp,
                 int num_envp, char *buffer, int buffer_size);
  void (*release)(struct class_device *dev);
  void (*class_release)(struct class *class);
};

19.2.2 class_simple_create #

struct class_simple *class_simpel_create(struct module *owner, char *name)

/!\ 커널 버전 2.6.13부터 사용하지 않는다.

19.2.3 class_simple_destroy #

void class_simple_destroy(struct class_simple *cs)

/!\ 커널 버전 2.6.13부터 사용하지 않는다.

19.2.4 class_simple_device_add #

struct class_device *class_simple_device_add(struct class_simple *cs, dev_t dev,
                                              struct device, const char *fmt, ...)

/!\ 커널 버전 2.6.13부터 사용하지 않는다.

19.2.5 class_simple_device_remove #

void class_simple_device_remove(dev_t dev)

/!\ 커널 버전 2.6.13부터 사용하지 않는다.

19.2.6 class_create #

struct class *class_create(struct module *owner, char *name)

사용예는 class_destroy를 참고하라.

관련함수: class_destroy

19.2.7 class_destroy #

void class_destroy(struct class *cls)

사용예는 다음과 같다:
struct *xxx_class;
EXPORT_SYMBOL(xxx_class);

static void __exit cleanup_xxx(void)
{
  ...
  class_destroy(xxx_class);
}

static int __init init_xxx(void)
{
  ...
  xxx_class = class_create(THIS_MODULE, "xxx");
  if (IS_ERR(sound_class))
    return PTR_ERR(sound_class);
  
  return 0;
}

관련함수: class_create

19.2.8 class_device_create #

class device를 생성한다. 내부에서 class_device 구조체를 정의하고 class_device_register를 통해 등록하게 된다.

struct class_device *class_device_create(struct class *cls, dev_t devt,
                                         struct device *device, char *fmt, ...)

예시:
다음은 sound/sound_core.c의 sound_insert_unit() 함수에서 사용된 예이다.
static int sound_insert_unit(...)
{
  ...
  if (...)
    ...
  else
    sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
  ...
  class_device_create(sound_class, MKDEV(SOUND_MAJOR, s->unit_minor),
                      NULL, s->name+6);
  ...
}

19.2.9 class_device_destroy #

등록된 class device를 찾아 class_device_unregister로 넘겨 등록을 해제한다.

void class_device_destroy(struct class *cls, dev_t devt)

예시:
다음은 sound/sound_core.c의 sound_remove_unit()함수에서 사용된 예이다.
static void sound_remove_unit(...)
{
  ...
  if (p) {
    ...
    class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
    ...
  }
}

19.2.10 class_register #

int class_register(struct class *cls);

19.2.11 class_unregister #

void class_unregister(struct class *cls);

19.2.12 class_get #

struct class * class_get(struct class *cls);

19.2.13 class_put #

void class_put(struct class *cls);

19.2.14 struct class_attribute #

struct class_attribute {
  struct attribute attr;
  ssize_t (*show)(struct class *, char *buf);
  ssize_t (*store)(struct class *, const char *buf, size_t count);
};

19.2.15 CLASS_ATTR #

CLASS_ATTR(name, mode, show, store);

19.2.16 class_create_file #

int class_create_file(struct class *cls, const struct class_attribute *attr);

19.2.17 class_remove_file #

void class_remove_file(struct class *cls, const struct class_attribue *attr);

19.2.18 struct class_device #

struct class_device {
  struct list_head node;

  struct kobject kobj;
  struct class *class;
  dev_t devt;
  struct class_device_attribute *devt_attr;
  struct device *dev;
  void *class_data;

  char class_id[BUS_ID_SIZE];
};

필드설명
node
kobj
class필수
devtsysfs에서 "dev"를 만든다.
devt_attr
dev선택사항
class_data현 클래스와 관련된 사용자 데이터를 위한 포인터
class_id현 클래스를 지시하는 고유 ID

19.2.19 class_get_devdata #

dev 라는 구조체 안의 class_data가 가리키는 포인터 값을 반환 받는다. 이는 class_set_data()를 통해 설정한 데이터에 대한 포인터 값을 가져와서 사용할 수 있도록 한다.

void * class_get_devdata(struct class_device *dev);

관현 함수 : class_set_devdata

19.2.20 class_set_devdata #

void class_set_devdata(struct class_device *dev, void *data);

사용자 class_device와 관련하여 사용자 데이터를 사용하고자 할 때, 그 데이터에 대한 포인터(data)를 class_device 내의 class_data에 포인터를 저장할 수 있다.

관련 함수 : class_get_devdata

19.2.21 class_devce_register #

int class_device_register(struct class_device *);

19.2.22 class_device_unregister #

void class_device_unregister(struct class_device *);

19.2.23 class_device_initialize #

void class_device_initialize(struct class_device *);

19.2.24 class_device_add #

int class_device_add(struct class_device *);

19.2.25 class_device_del #

void class_device_del(struct class_device *);

19.2.26 class_device_rename #

int class_device_rename(struct class_device *, char *);

19.2.27 class_device_get #

struct class_device * class_device_get(struct class_device *);

19.2.28 class_device_put #

void class_device_put(struct class_device *);

19.2.29 struct classs_device_attribute #

struct class_device_attribute {
  struct attribute        attr;
  ssize_t (*show)(struct class_device *, char * buf);
  ssize_t (*store)(struct class_device *, const char * buf, size_t count);
};

19.2.30 CLASS_DEVICE_ATTR #

CLASS_DEVICE_ATTR(_name,_mode,_show,_store)

19.2.31 class_device_create_file #

int class_device_create_file(struct class_device *,
                             const struct class_device_attribute *);

19.2.32 class_device_remove_file #

void class_device_remove_file(struct class_device *,
                               const struct class_device_attribute *);

19.2.33 class_device_create_bin_file #

int class_device_create_bin_file(struct class_device *, struct bin_attribute *);

19.2.34 class_device_remove_bin_file #

void class_device_remove_bin_file(struct class_device *, struct bin_attribute *);

19.2.35 struct class_interface #

struct class_interface {
  struct list_head node;
  struct class *class;
  
  int (*add) (struct class_device *)
  void *(remove) (struct class_device *);
};

19.2.36 class_interface_register #

int class_interface_register(struct class_interface *);

19.2.37 class_interface_unregister #

void class_interface_unregister(struct class_interface *);

19.3 class의 사용 예 #

다음은 커널 버전 2.6.13의 drivers/mtd/mtdchar.c에 포함된 내용 중 class와 관련된 부분을 중심으로 살펴본다.

19.3.1 클래스 생성 #

static int __init init_mtdchar(void)
{
  if (register_chrdev(...)) {
    ...
  }

  mtd_class = class_create(THIS_MODULE, "mtd");

  if (IS_ERR(mtd_class)) {
    ...
  }

  register_mtd_user(&notifier);
  return 0;
}

class_create 함수에 의하여 sysfs 상에 /sys/class/mtd 가 만들어진다.

19.3.2 클래스 디바이스 생성/제거 #

클래스 디바이스를 생성하기 위하여 mtd에서는 mtd_notifier 구조체를 정의하고 그 안에 add와 remove라는 함수 포인터를 정의하였다. 이 함수 안에서 class_device_create/remove를 호출하도록 하고 있다.
static void mtd_notify_add(struct mtd_info *mtd)
{
  if (!mtd)
    return;

  class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
                      NULL, "mtd%d", mtd->index);
  class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
                      NULL, "mtd%dro", mtd->index);
}

static void mtd_notify_remove(struct mtd_info *mtd)
{
  if (!mtd)
    return;

  class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
  class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
}

static struct mtd_notifier notifier = {
  .add = mtd_notify_add,
  .remove = mtd_notify_remove,
};

이 두 add와 remove 함수는 register_mtd_user와 unregister_mtd_user 함수를 통해 호출이 된다.

19.3.3 클래스 제거 #

static void __exit cleanup_mtdchar(void)
{
  unregister_mtd_user(&notifier);
  class_destroy(mtd_class);
  unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
}

unregister_mtd_user를 통해 notifier의 remove 함수를 호출하여 /sys/class/mtd/ 아래에 만들어진 mtd관련 디바이스 디렉토리를 삭제하게 된다.

이후 class_destroy를 통해 /sys/class 아래의 mtd 디렉토리까지 삭제하게 된다.

20 hotplug #

LDM에서는 hotplug event를 처리하기 위한 API를 제공하고 있다. hotplug를 처리하는 핸들러는 사용자 수준의 /sbin/hotplug 스크립트를 실행하는데, 이때 환경 변수를 통해 적당한 값들을 넘기게 된다.

이들 값들은 /sbin/hotplug 스크립트를 통해 /etc/hotplug.d 등에 있는 적당한 스크립트를 실행하여 그 값에 따른 처리를 하게 된다.

21 udev #

리눅스의 디바이스를 접근하기 위하여 파일로써 특별한 노드를 생성하였다. 아직까지 이 방법을 사용하고 있으며, 커널 2.3 이후 버전에서는 devfs라는 커널 레벨에서 노드를 생성 시켜주는 디바이스 파일 시스템이라는 번잡한 방법을 제공하기 시작했다.

그러나 이 방법은 기존의 문제를 그대로 안고 있으며, 커널과 사용자 간의 디바이스 관리를 혼란스럽게 만들고 있다. 이런 이유로 이 devfs라는 더 이상 사용하지 않을 것으로 보이고 있다.

이후 대안으로 제시된 것이 커널 2.5 버전 이후에 적용되고 있는 udev 라는 디바이스 시스템이다. 이는 장기적인 관점에서 접근하고 있으며, 아직까지는 초보적인 수준의 기능을 제공하고 있다.

그러나 궁극적으로는 udev를 추구하고 있다.

udev의 궁극적인 목표는 다음과 같다:
  • /dev를 대체할 동적 방법론
  • 디바이스 이름 생성 방법론
  • 시스템 디바이스(sysfs)에서 정보를 취합할 API를 제공이다.

현재 이러한 목표를 이루기 위하여 크게 세 부분으로 나눠 작업이 이뤄지고 있다.
  • udev - /dev를 대체할 동적 방법론
  • namedev - 디바이스 이름 생성 방법론
  • libsysfs - sysfs에 대한 common API

udev는 앞서 설명한 것처럼 sysfs와 밀접한 관계가 있으며, 각 디바이스에 탐지/인식/제거/변경 등의 event를 커널 레벨에서 받아 처리하기 위하여 hotplug 기능이 필수적이다.

이러한 모든 기초가 되는 것이 현재 2.6에서 차근 차근 준비가 되어 가고 있으며, 일부 기능은 현재도 사용 가능하다.

22 firmware #

일부 디바이스의 경우엔 firmware를 로드하거나 업데이트할 필요가 있다. 이러한 경우를 위하여 리눅스 2.6의 sysfs를 이용하여 사용자 수준에서 firmware 데이터를 커널 수준으로 로드할 수 있다.

22.1 firmware API #

firmware와 관련된 것들은 에 선언되어 있다.

함수설명
request_firmware
request_firmware_nowait
release_firmware

22.1.1 struct fw #

firmware 인터페이스에서 사용하는 기본 자료형은 struct fw;가 있다.
struct firmware {
  size_t size;
  u8 *data;
};

22.1.2 request_firmware #

sysfs를 통해 사용자 수준의 데이터를 커널 수준으로 넘겨 받는다.

int request_firmware(const struct firmware **fw, char *name, struct device *device);

넘겨 받은 데이터는 fw 구조체 내에 저장이 된다. 넘겨 받은 firmware 데이터의 이름은 name으로 설정한다. device는 firmware와 관련된 디바이스이다.

실제 데이터는 fw->data에 있으며, 그 크기는 fw->size가 되는 것이다. 따라서 이 값을 디바이스에 로드하거나 업데이트하면 된다.

22.1.3 request_firmware_nowait #

request_firmware는 firmware를 요구 받았을 때 완료되기까지 휴면상태가 되는데, request_firmware_nowait는 휴면되지 않는다.

int request_firmware_nowait(struct module *module, char *name, struct device *device,
                            void *context, void (*cont)(const struct firmware *fw,
                                                        void *context));

22.1.4 release_firmware #

request_firmware를 통해 받은 firmware 데이터에 대한 자원 할당을 해제한다.

void release_firmware(struct firmware *fw);

22.2 firmware의 사용 #

사용자 수준에서는 /sys/class/firmware 디렉토리 아래에 디바이스 이름의 디렉토리가 만들어진다. 그 아래에 loading, data, device 등의 파일 또는 심볼릭 링크가 만들어진다.

loading은 현재 firmware의 로딩이 완료되면 0을 쓰고, 로딩을 중단한다면 -1, 이외의 값은 로딩 중임을 의미한다.

data를 통해 firmware 데이터를 write하면 된다.

device는 /sys/device에서 해당 디바이스에 대한 심볼릭 링크이다.

경우에 따라서 firmware를 로딩하는 시간이 길어지면 timeout이 걸리는데, 이는 /sys/class/fimrware/timeout를 통해 timeout 시간을 변경할 수 있다.

23 References #


last modified 2008-07-15 08:48:03
EditText|FindPage|DeletePage|LikePages Valid XHTML 1.0! Valid CSS! powered by MoniWiki
0.2196 sec