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

Kernel API 3 - Linux Driver Model #


-- 김도집 (2005-09-28)

Contents

1 Kernel API 3 - Linux Driver Model
1.1 리눅스 디바이스 드라이버 개요
1.2 디바이스 드라이버의 객체 개념
1.2.1 subsystem, kset, ktype, kobj의 관계
1.3 kref
1.3.1 kref API
1.3.1.1 struct kref
1.3.1.2 kref_init
1.3.1.3 kref_get
1.3.1.4 kref_put
1.4 kobject
1.4.1 kobject API
1.4.1.1 kobject_set_name
1.4.1.2 kobject_name
1.4.1.3 kobject_init
1.4.1.4 kobject_cleanup
1.4.1.5 kobject_add
1.4.1.6 kobject_del
1.4.1.7 kobject_register
1.4.1.8 kobject_unregister
1.4.1.9 kobject_get
1.4.1.10 kobject_put
1.4.1.11 kobject_get_path
1.5 kset
1.5.1 kset API
1.5.1.1 struct kset
1.5.1.2 kset_init
1.5.1.3 kset_add
1.5.1.4 kset_register
1.5.1.5 kset_unregister
1.5.1.6 to_kset
1.5.1.7 kset_get
1.5.1.8 kset_put
1.6 ktype
1.7 sysfs
1.7.1 /sys
1.7.2 sysfs
1.7.2.1 sysfs_create_dir
1.7.2.2 sysfs_remove_dir
1.7.2.3 sysfs_rename_dir
1.7.2.4 sysfs_create_file
1.7.2.5 sysfs_update_file
1.7.2.6 sysfs_remove_file
1.7.2.7 sysfs_create_link
1.7.2.8 sysfs_remove_link
1.7.2.9 sysfs_create_bin_file
1.7.2.10 sysfs_remove_bin_file
1.7.2.11 struct attribute_group
1.7.2.12 sysfs_create_group
1.7.2.13 sysfs_remove_group
1.7.3 사용자를 위한
1.7.4 sysfs를 이용한 dev 노드 만들기
1.8 subsystem
1.8.1 subsystem 개요
1.8.2 subsystem과 kset과의 관계
1.8.3 subsystem on sysfs
1.8.4 subsystem API
1.8.4.1 decl_subsys
1.8.4.2 subsystem_init
1.8.4.3 subsystem_register
1.8.4.4 subsystem_unregister
1.8.4.5 subsystem_get
1.8.5 subsystem_put
1.9 kobj_map
1.9.1 kobj_map API
1.9.1.1 kobj_probe_t
1.9.1.2 struct kboj_map
1.9.1.3 kobj_map
1.9.1.4 kobj_unmap
1.9.1.5 kobj_lookup
1.9.1.6 kobj_map_init
1.10 bus
1.10.1 bus on sysfs
1.10.2 bus API
1.10.2.1 bus_register
1.10.2.2 bus_unregister
1.10.2.3 bus_for_each_dev
1.10.2.4 bus_for_each_drv
1.10.2.5 BUS_ATTR
1.10.2.6 bus_create_file
1.10.2.7 bus_remove_file
1.11 device
1.11.1 device on sysfs
1.11.2 device API
1.11.2.1 struct device
1.11.2.2 device_register
1.11.2.3 device_unregister
1.11.2.4 DEVICE_ATTR
1.11.2.5 device_create_file
1.11.2.6 device_remove_file
1.11.2.7 device_initalize
1.11.2.8 device_add
1.11.2.9 device_del
1.12 driver
1.12.1 driver API
1.12.1.1 struct device_driver
1.12.1.2 driver_register
1.12.1.3 driver_unregister
1.12.1.4 DRIVER_ATTR
1.12.1.5 driver_create_file
1.12.1.6 driver_remove_file
1.13 class
1.13.1 class on sysfs
1.13.2 class
1.13.3 클래스 등록하기
1.13.3.1 struct class
1.13.3.2 struct class_device
1.13.3.3 class_simple_create (obsolete)
1.13.3.4 class_simple_destroy (obsolete)
1.13.3.5 class_simple_device_add (obsolete)
1.13.3.6 class_simple_device_remove (obsolete)
1.13.3.7 class_create
1.13.3.8 class_destroy
1.13.3.9 class_device_create
1.13.3.10 class_device_destroy
1.13.3.11 class_register
1.13.3.12 class_unregister
1.13.3.13 class_get
1.13.3.14 class_put
1.13.3.15 struct class_attribute
1.13.3.16 CLASS_ATTR
1.13.3.17 class_create_file
1.13.3.18 class_remove_file
1.13.3.19 class_get_devdata
1.13.3.20 class_set_devdata
1.13.3.21 class_devce_register
1.13.3.22 class_device_unregister
1.13.3.23 class_device_initialize
1.13.3.24 class_device_add
1.13.3.25 class_device_del
1.13.3.26 class_device_rename
1.13.3.27 class_device_get
1.13.3.28 class_device_put
1.13.3.29 struct classs_device_attribute
1.13.3.30 CLASS_DEVICE_ATTR
1.13.3.31 class_device_create_file
1.13.3.32 class_device_remove_file
1.13.3.33 class_device_create_bin_file
1.13.3.34 class_device_remove_bin_file
1.13.3.35 struct class_interface
1.13.3.36 class_interface_register
1.13.3.37 class_interface_unregister
1.13.4 class의 사용 예
1.13.4.1 클래스 생성
1.13.4.2 클래스 디바이스 생성/제거
1.13.4.3 클래스 제거
1.14 hotplug
1.15 udev
1.16 References

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

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

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

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

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

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

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

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

객체라고 하면 흔히 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를 사용자 영역으로 넘겨줌으로써 디바이스의 상태 변화를 알려줘 적절한 조치를 취할 수 있도록 하고 있다. 이는 사용중 착탈식이 가능한 디바이스에 매우 유용한 기능 중 하나이다.

1.2.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을 포인트하도록 한다.

1.3 kref #

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

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

struct kref {
  atomic_t refcount;
};

위 내용에서 atomic_t라는 자료형이 있다. 이는 원자적 처리를 보장해 주는 자료형이다. 원자라 함은 더이상 쪼개질 수 없는 것(양자물리학에서는 원자도 더 쪼개질 수 있다)으로 원자적이라 함은 다른 처리가 중간에 끼어들 수 없을 의미한다.즉 다른 어떤 방해도 없이 한번에 처리됨을 의미한다.

1.3.1 kref API #

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

1.3.1.1 struct kref #

struct kref {
  atomic_t refcount;
};

1.3.1.2 kref_init #

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

void kref_init(struct kref *kref);

1.3.1.3 kref_get #

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

void kref_get(struct kref *kref);

1.3.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));

1.4 kobject #

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

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을 지시하도록 한다.

1.4.1 kobject API #

함수설명
kobject_set_namekobject의 이름을 지정한다.
kobject_namekobject의 이름을 가져온다.
kobject_initkobject을 초기화한다.
kobject_cleanupkobject을 위해 할당 받은 자원을 반환한다.
kobject_addkobject을 kset 리스트에 추가한다.
kobject_delkobject을 kset 리스트에서 제거한다.
kobject_registerkobject을 초기화 하고 kset에 추가한다.
kobject_unregisterkobject을 리스트에서 제거한다.
kobject_getkobject의 ref값을 증가시킨다.
kobject_putkobject의 ref값을 감소시킨다.
kobject_get_pathkobject의 경로명을 가져온다.

1.4.1.1 kobject_set_name #

kobj의 이름을 설정한다. <linux/kobject.h>에 선언되어 있다.

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;

1.4.1.2 kobject_name #

<linux/kobject.h>에 선언되어 있다.

kobj의 k_name를 반환한다.

char * kobject_name(struct kobject *kobj);

1.4.1.3 kobject_init #

<linux/kobject.h>에 선언되어 있다.

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);

1.4.1.4 kobject_cleanup #

<linux/kobject.h>에 선언되어 있다.

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

vodi kobject_cleanup(struct kobject *);

1.4.1.5 kobject_add #

자신이 속한 kset에 kobj을 추가한다. <linux/kobject.h>에 선언되어 있다.

int kobject_add(struct kobject *kobj);

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

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

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

1.4.1.6 kobject_del #

<linux/kobject.h>에 선언되어 있다.

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

void kobject_del(struct kobject *);

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

1.4.1.7 kobject_register #

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

int kobject_register(struct kobject *);

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

1.4.1.8 kobject_unregister #

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

void kobject_unregister(struct kobject *);

1.4.1.9 kobject_get #

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

struct kobject *kobject_get(struct kobject *);

관련함수: kobject_put

1.4.1.10 kobject_put #

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

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

관련함수: kobject_get

1.4.1.11 kobject_get_path #

<linux/kobject.h>에 선언되어 있다.

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

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

/!\ (주의) 리턴 받은 kobject의 경로명은 내부적으로 kmalloc을 통해 할당 받은 메모리에 저장된 것이다.

1.5 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

1.5.1 kset API #

함수설명
kset_initkset을 초기화 한다.
kset_addkset(kset->kobj)을 리스트에 추가한다.
kset_registerkset_init과 ket_add를 한 것과 동일
kset_unregisterkset(kset->kobj)을 리스트에서 제거한다.
to_ksetkobj을 포함하는 kset을 구한다.
kset_getkset내의 kobj의 ref값을 증가시킨다.
kset_putKset내의 kobj의 ref값을 감소시킨다.

1.5.1.1 struct kset #

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

/!\ 커널 버전 2.6.16부터 kset_hotplug_ops *hotplug_ops가 kset_uevent_ops *uevent_ops로 이름이 변경되었다. 그리고 spinlock_t list_lock이 추가되었다.

struct kset {
  struct subsystem *subsys;
  struct kobj_type *ktype;
  struct list_head list;
  spinlock_t       list_lock;
  struct kobject kobj;
  struct kset_uevent_ops *uevent_ops;
};

필드설명
subsyskset을 포함하는 subsys
ktypekset의 ktype
listkset의 리스트를 위한 entry 포인터
kobjkset의 kobj
uevent_ops이벤트를 위한 operations

1.5.1.2 kset_init #

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

void kset_init(struct kset *k);

1.5.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를 호출하기도 한다.

1.5.1.4 kset_register #

<linux/kobjec.h>에 선언되어 있다.

kset_init과 kset_add를 함께 사용한 것과 동일한 역할을 한다. 즉, kset을 초기화해서 kset을 추가한다. kset을 추가한다는 것은 엄밀히 말하면 kset내의 kobj을 등록하는 것이다.

int kset_register(struct kset *k);

관련함수: kset_unregister

1.5.1.5 kset_unregister #

<linux/kobject.h>에 선언되어 있다.

kset의 등록을 해제한다.

void kset_unregister(struct kset *k);

관련함수: kset_register

1.5.1.6 to_kset #

<linux/kobject.h>에 선언되어 있다.

to_kset은 kobj을 알고 있을 때 이를 포함하고 있는 kset을 구하고자 할 때 사용한다.

struct kset *to_kset(struct kobject *kobj);

1.5.1.7 kset_get #

<linux/kobject.h>에 선언되어 있다.

kset내의 kobj의 ref 값을 증가시킨다.

struct kset *kset_get(struct kset *k);

관련함수: kset_put

1.5.1.8 kset_put #

<linux/kobject.h>에 선언되어 있다.

kset내의 kobj의 ref 값을 감소 시킨다.

void kset_put(struct kset *k);

관련함수: kset_get

1.6 ktype #

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

1.7 sysfs #

1.7.1 /sys #

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

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

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

1.7.2 sysfs #

<linux/sysfs.h>에 선언되어 있다.

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_fileattribute을 만든다.
sysfs_update_fileattribute의 변경 내용을 갱신한다.
sysfs_remove_fileattribute을 제거한다.
sysfs_create_link심볼릭 링크를 만든다.
sysfs_remove_link심볼릭 링크를 제거한다.
sysfs_create_bin_file바이너리 attribute 파일을 만든다.
sysfs_remove_bin_file바이너리 attribute 파일을 지운다.
sysfs_create_group여러개의 attribute을 만든다.
sysfs_remove_group여러개의 attribute을 제거한다.

1.7.2.1 sysfs_create_dir #

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

int sysfs_create_dir(struct kobject *kobj);

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

1.7.2.2 sysfs_remove_dir #

객체를 위해 만들어진 디렉토리를 삭제한다.

void sysfs_remove_dir(struct kobject *kobj);

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

1.7.2.3 sysfs_rename_dir #

객체를 위해 만들어진 디렉토리 이름을 변경한다.

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

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

1.7.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;
}

1.7.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를 보고 어떤 변화가 있음을 감지할 수 있을 것이다.

1.7.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);
  }
}

1.7.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

1.7.2.8 sysfs_remove_link #

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

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

1.7.2.9 sysfs_create_bin_file #

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

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

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

1.7.2.10 sysfs_remove_bin_file #

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

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

1.7.2.11 struct attribute_group #

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

이는 <linux/sysfs.h>에 정의되어 있다.

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

1.7.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);
}

1.7.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);
}

1.7.3 사용자를 위한 #

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

1.7.4 sysfs를 이용한 dev 노드 만들기 #

sysfs 상에는 각 디바이스에 대한 주요 정보를 제공하고 있다. 이를 이용하여 디바이스의 노드를 자동으로 수집하여 노드를 생성할 수도 있다.

노드 생성을 위한 간단한 스크립트이다.
find /sys/block /sys/class -name dev -printf '%h\n' |
while read d
do
    n=${d//*\//}
    i=$(<$d/dev)
    mknod $n ${d:5:1} ${i/:/ }
done

1.8 subsystem #

1.8.1 subsystem 개요 #

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

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

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

1.8.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;
  ...
};

1.8.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 등이 있다.

1.8.4 subsystem API #

함수설명
decl_subsys
subsystem_init
subsystem_register
subsystem_unregister
subsystem_get
subsystem_put

1.8.4.1 decl_subsys #

새로운 subsystem을 정의한다. <linux/kobject.h>에 선언되어 있다.

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로 선언하여 생략해도 무방하다.

1.8.4.2 subsystem_init #

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

<linux/kobject.h>에 선언되어 있다.

void subsystem_init(struct subsystem *subsys);

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

1.8.4.3 subsystem_register #

새로운 subsystem을 등록한다. <linux/kobject.h>에 선언되어 있다.

int subsystem_register(struct subsystem *s);

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

1.8.4.4 subsystem_unregister #

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

void subsystem_unregister(struct subsystem *subsys);

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

1.8.4.5 subsystem_get #

struct subsystem *subsys_get(struct subsystem *subsys);

1.8.5 subsystem_put #

void subsys_put(struct subsystem *subsys);

1.9 kobj_map #

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

디바이스는 크게 문자, 블록, 네트워크 등으로 구분이 된다. 특히 문자와 블록 디바이스는 디바이스 노드를 갖게 되는데, 이때 주번호와 부번호를 갖게 된다. 이들을 디바이스 번호라고 한다.

문자 디바이스 번호는 cdev_map이라는 kobj_map형의 전역으로 선언된 테이블에서 관리한다. 블록 디바이스 번호는 bdev_map을 통해 같은 방식으로 관리된다.

이들에 대해 자세한 내용은 kobj_map API에서 설명한다.

1.9.1 kobj_map API #

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

1.9.1.1 kobj_probe_t #

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

1.9.1.2 struct kboj_map #

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

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;
};

1.9.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);
}

1.9.1.4 kobj_unmap #

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

1.9.1.5 kobj_lookup #

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

domain->probes는 256개의 테이블을 갖고 있다. dev의 major 번호를 255로 나눈 나머지를 갖고 테이블의 엔트리를 결정하고 그 엔트리 내에서 연결 리스트로 연결되어 있는 디바이스의 정보 중 dev 번호 범위에 들어가는 값을 결정하게 된다.

예시:
다음은 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;
}

1.9.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);
  ...
}

1.10 bus #

버스는 개별 디바이스에 대해 포괄적인 상위 개념이다. 예를 들어 USB 디바이스는 그 상위 개념인 버스를 통해 개별 디바이스를 관리하게 된다.

<linux/device.h>에 관련 자료형 및 함수가 선언되어 있다.

struct bus_type {
  const char       *name;

  struct subsystem subsys;
  struct kset      drivers;
  struct kset      devices;
  struct klist     klist_devices;
  struct klist     klist_drivers;

  struct bus_attribute    *bus_attrs;
  struct device_attribute *dev_attrs;
  struct driver_attribute *drv_attrs;

  int (*match)(struct device *dev, struct device_driver *drv);
  int (*uevent)(struct device *dev, char **envp,
                int num_envp, char *buffer, int buffer_size);
  int (*probe)(struct device *dev);
  int (*remove)(struct device *dev);
  int (*shutdown)(struct device *dev);
  int (*suspend)(struct device *dev, pm_message_t state);
  int (*resume)(struct device *dev);
};

1.10.1 bus on sysfs #

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

1.10.2 bus API #

함수설명
bus_register
bus_unregister
bus_for_each_dev
bus_for_each_drv
BUS_ATTR
bus_create_file
bus_remove_file

1.10.2.1 bus_register #

<linux/device.h>에 선언되어 있다.

새 버스를 등록한다.

int bus_register(struct bus_type *bus);

관련함수: bus_unregister

버스를 등록하기 위해서는 bus_type형의 bus 데이터가 먼저 선언되어야 한다. 다음은 mmc 드라이버에서 mmc를 버스로 등록하는 한 예이다(drivers/mmc/mmc_sysfs.c).
static struct bus_type mmc_bus_type = {
  .name       = "mmc",
  .dev_attrs  = mmc_dev_attrs,
  .match      = mmc_bus_match,
  .uevent     = mmc_bus_uvent,
  .probe      = mmc_bus_probe,
  .remove     = mmc_bus_remove,
  .suspend    = mmc_bus_suspend,
  .resume     = mmc_bus_resume,
};

static init __init mmc_init(void)
{
  int ret = bus_register(&mmc_bus_type);
  if (ret == 0) {
    ret = class_register(&mmc_host_class);
    if (ret)
      bus_unregister(&mmc_bus_type);
  }
  return ret;
}

1.10.2.2 bus_unregister #

<linux/device.h>에 선언되어 있다.

버스의 등록을 해제한다.

void bus_unregister(struct bus_type *bus);

관련함수: bus_register

1.10.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 *));

1.10.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 *));

1.10.2.5 BUS_ATTR #

BUS_ATTR(name, mode, show, store);

1.10.2.6 bus_create_file #

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

1.10.2.7 bus_remove_file #

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

1.11 device #

하나의 물리적인 디바이스는 디바이스 구조체에 의해 표현될 수 있다. 디바이스가 어떤 제어를 처리하는 하지는 않고 단지 디바이스에 대한 정보를 갖는다.

디바이스를 표현하는 구조체는 struct device이다. 이에 대한 자세한 내용은 API 부분에서 설명한다.

1.11.1 device on sysfs #

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

1.11.2 device API #

새로운 디바이스를 등록할 때 사용한다.

함수설명
device_register디바이스를 등록한다.
device_unregister디바이스 등록을 해제한다.
DEVICE_ATTRdevice_attribute 구조체를 만드는 helper 매크로
device_create_filesysfs에 attribute를 등록한다.
device_remove_filesysfs에 등록된 attribute를 삭제한다.
device_initializedevice 구조체를 초기화한다.
device_add디바이스 계층상에 디바이스를 추가한다.
device_del디바이스 계층상에서 디바이스를 제거한다.

1.11.2.1 struct device #

device의 기본 데이형은 struct device;이다. 이는 <linux/device.h>에 선언되어 있다.

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);
};

1.11.2.2 device_register #

device_initialize와 device_add를 실행한 결과와 동일하다. 즉 새 디바이스를 등록한다.
int device_register(struct device *dev);

관련함수: device_unregister

1.11.2.3 device_unregister #

등록한 디바이스를 제거한다.

void device_unregister(struct device *dev);

관련함수: device_register

1.11.2.4 DEVICE_ATTR #

device_attribute 구조체를 쉽게 정의할 수 있도록 해주는 매크로이다. 이는 <linux/device.h>에 정의되어 있다.

DEVICE_ATTR(name, mode, show, store);
name
device의 이름을 지정한다. ""을 사용하지 않도록 주의한다.

mode
sysfs상에 생성되는 파일의 접근 권한을 지정한다. 커널 상에서 사용할수 있도록 매크로 형태로 정의되어 있는데, 이는 <linux/stat.h>에 있다. 매크로는 S_IRWXUGO, S_IALLUGO, S_IRUGO, IWUGO, S_IXUGO가 있다.

show
sysfs상에서 파일을 읽을 때 실행된다. 없다면 NULL로 지정할 수 있다.

store
sysfs상에서 파일을 쓸 때 실행된다. 없다면 NULL로 지정할 수 있다.

예를 들어 DEVICE_ATTR(xxx, S_IRUGO, show_xxx, NULL) 이라고 한다면 dev_attr_xxx 라는 이름의 구조체가 만들어지며 읽기만 가능하고 읽을 경우 이에 대한 처리는 show_xxx에서 처리하게 된다. 물론 show_xxx 함수가 미리 정의되어 있어야 한다.

show 및 store 함수의 원형은 다음과 같다.
ssize_t show(struct device *dev, struct device_attribute *attr,
                        char *buf);
ssize_t store(struct device *dev, struct device_attribute *attr,
                         const char *buf, size_t count);
이들의 버퍼 크기는 PAGE_SIZE를 넘지 않아야 하며 buf에 쓴 데이터의 바이트 길이를 리턴 값으로 넘겨주어야 한다.

1.11.2.5 device_create_file #

디바이스에 대한 atrribute를 만들면 이를 sysfs에 등록해야 한다. 이렇게 하면 해당하는 sysfs의 계층에 파일이 생성된다. 이때 sysfs에 파일을 등록하는 것은 device_create_file을 이용한다.

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

관련함수: DEVICE_ATTR, device_remove_file

1.11.2.6 device_remove_file #

attribute를 등록했다면 이를 해제할 때 사용한다.

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

관련함수: DEVICE_ATTR, device_create_file

1.11.2.7 device_initalize #

device 구조체를 초기화 시킨다. 새로운 계층(종류)의 디바이스를 추가하는 경우에 device 구조체를 초기화한다. 이 디바이스는 devices_subsys라는 서브시스템에 속하게 된다.

<linux/device.h>에 선언되어 있다.
void device_initialize(struct device *dev);

1.11.2.8 device_add #

디바이스 계층상에 디바이스를 추가한다. device_add를 사용하기 전에 해당 dev가 device_initalize를 통해 초기화가 이뤄져야 하며 반드시 bus_id가 지정되어 있어야 한다. bus_id는 보통 디바이스 이름으로 문자열로 표현된다.

int device_add(struct device *dev);

device_initialize와 device_add와 같은 일을 하는 것이 바로 device_register이다.

관련함수: deivice_initialize, device_del

1.11.2.9 device_del #

device_del은 디바이스 계층상에서 디바이스를 제거한다.
void device_del(struct device *dev);

관련함수: device_add

1.12 driver #

물리적인 디바이스에 대한 정보는 device 구조체에 의해 표현된다고 앞서 설명하였다. 디바이스는 하나의 드라이버를 갖게 되는데, 이 드라이버에 대한 정보는 struct device_driver에 의해 기술된다.

device_driver에서는 최초 디바이스를 인식한 후 부터 디바이스가 제거 될 때까지의 처리를 담당하는 제어 함수들에 대한 정보를 갖고 있다. 이를 통해 실질적인 디바이스의 동작을 기술하게 된다.

device_driver 구조체에 대한 자세한 내용은 API 장에서 자세히 설명한다.

1.12.1 driver API #

함수설명
driver_register
driver_unregister
DRIVER_ATTR
driver_create_file
driver_remove_file

1.12.1.1 struct device_driver #

device_driver 구조체는 디바이스 드라이버에 대한 정보를 담고 있는 기본적인 자료형이다. 이는 <linux/device.h>에 정의되어 있다. 원형은 다음과 같다.
struct device_driver {
  char *name;
  struct bus_type *bus;

  struct completion unloaded;
  struct kobject kobj;
  struct list_head devices;

  struct module *owner;

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

/!\ 커널 버전 2.6.15 부터 suspend와 resume에 대한 것이 다음과 같이 변경되었다. 그리고 리스트에 대한 선언도 변경되었다. struct list_head devices; 대신 아래와 같이 변경되었다.
  struct klist      klist_devices;
  struct klist_node knode_bus;

  int (*suspend)    (struct device *dev, pm_message_t state);
  int (*resume)     (struct device *dev);

전원 관리(power management)와 관련하여 크게 4개의 상태가 있다.
상태설명
ON드라이버가 동작하는 상태
FREEZE모든 동작이 중단된 상태이지만 전원 공급은 정상적으로 이뤄지는 상태
SUSPEND모든 동작이 중단되고 절전 모드인 상태
??

1.12.1.2 driver_register #

int driver_register(struct device_driver *drv);

1.12.1.3 driver_unregister #

void driver_unregister(struct device_driver *drv);

1.12.1.4 DRIVER_ATTR #

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

DRIVER_ATTR(name, mode, show, store);

1.12.1.5 driver_create_file #

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

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

1.12.1.6 driver_remove_file #

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

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

1.13 class #

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

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

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

1.13.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

1.13.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_destroy
  • class_device_create

1.13.3 클래스 등록하기 #

새 클래스를 등록하기 위해서는 class와 class_device 구조체형의 데이터를 등록해야만 한다. class 데이터를 등록할 때는 class_reigster를 이용하고, class_device를 등록할 때는 class_device_register를 이용한다.

또 클래스를 등록하는 다른 방법으로는 class_create 함수를 이용하는 것이다. class_create보다 상위 함수로 class의 이름만 지정해 주면 내부적으로 class 구조체를 생성하고 그 class 구조체의 포인터를 반환한다. 클래스 디바이스에 대한 등록은 앞서 class_device_register를 설명한 방법과 동일하다.

이러한 클래스와 클래스 디바이스만을 등록하는 것이 의미가 있는 것은 아니다. 실제로는 sysfs에 attribute를 위한 파일을 등록할 필요가 있는데, 이때는 class_device_create_file을 이용하여 클래스 디바이스에 대한 attribute를 등록한다.

자세한 내용은 관련 함수를 참조하자.

1.13.3.1 struct class #

class의 기본 자료형은 struct class;이다. 이는 <linux/device.h>에 정의되어 있다.

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);
};

1.13.3.2 struct class_device #

하나의 클래스가 등록이 되면 이 클래스에 속하는 하나 이상의 클래스 디바이스가 등록될 수 있다. 이때 이들 디바이스를 기술하는 데이터 구조체가 필요한데, 그것이 바로 class_device이다.

class_device 구조체는 커널 2.6.15부터 그 내용이 일부 변경되었다.

커널 버전 2.6.15 이전:
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];
};

커널 버전 2.6.15 부터:
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;
  struct class_device *parent;

  void (*release)(struct class_device *dev);
  int  (*hotplug)(struct class_device *dev, char **envp,
                  int num_envp, char *buffer, int buffer_size);
  char class_id[BUS_ID_SIZE];
};

커널 2.6.15 이전과 비교하여 parent와 release, hotplug가 추가되었다. 클래스 디바이스의 계층적인 구조를 지원하기 위하여 parent 필드가 추가되었다.

필드설명
node내부적으로 드라이버 코어에서만 사용된다.
kobj내부적으로 드라이버 코어에서만 사용된다.
class이 클래스 디바이스를 포함하는 클래스. 필수
devt내부적으로 드라이버 코어에서만 사용된다. sysfs에서 "dev"를 만든다.
devt_attr내부적으로 드라이버 코어에서만 사용된다.
devsysfs상에 만들어진 device 구조체를 심볼릭 링크한다. 선택사항
class_data현 클래스 내에 사용자 데이터를 저장하기 위한 포인터
parent이 클래스 디바이스를 포함하는 상위 클래스 디바이스
release클래스 다바이스에 대한 릴리즈 함수 포인터
hotplug클래스 디바이스에 대한 hotplug 함수 포인터
class_id현 클래스를 지시하는 고유 ID

1.13.3.3 class_simple_create (obsolete) #

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

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

1.13.3.4 class_simple_destroy (obsolete) #

void class_simple_destroy(struct class_simple *cs)

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

1.13.3.5 class_simple_device_add (obsolete) #

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

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

1.13.3.6 class_simple_device_remove (obsolete) #

void class_simple_device_remove(dev_t dev)

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

1.13.3.7 class_create #

<linux/device.h>에 선언되어 있다.

class_register보다 상위의 함수로 name의 클래스 생성하고 이를 등록한다.

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

사용예는 class_destroy를 참고하라.

관련함수: class_destroy

1.13.3.8 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

1.13.3.9 class_device_create #

<linux/device.h>에 선언되어 있다.

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);
  ...
}

1.13.3.10 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));
    ...
  }
}

1.13.3.11 class_register #

함수는 <inux/device.h>에 선언되어 있다.

새로운 클래스를 등록하기 위하여 class_register를 사용한다. 이렇게 등록된 클래시는 /sys/class/ 아래에 클래스 이름으로 등록이 된다.

클래스를 등록하기 위해서는 class 데이터를 정의해야만 한다. class 구조체의 내용 중 name과 release 함수만 정의해도 하나의 클래스를 만드는데 충분하다.

int class_register(struct class *cls);

다음은 /sys/class/backlight라는 클래스를 등록하는 간단한 예이다(drivers/video/backlight/backlight.c).
static void backlight_class_release(struct class_device *dev)
{
  struct backlight_device *bd = to_backlight_device(dev);
  kfree(bd);
}

static struct class backlight_class = {
  .name    = "backlight",
  .release = backlight_class_release,
};

static int __init backlight_class_init(void)
{
  return class_register(&backlight_class);
}

관련 함수: class_unregister

1.13.3.12 class_unregister #

함수는 <linux/device.h>에 선언되어 있다. 클래스의 등록을 해제한다.

void class_unregister(struct class *cls);

class_register를 통해 등록한 클래스 데이터를 인자를 넘겨주기만 하면 된다.

backlight 클래스에 대한 예를 보면 다음과 같다(drivers/video/backlight/backlight.c).

static void __exit backlight_class_exit(void)
{
  class_unregister(&backlight_class);
}

관련함수: class_register

1.13.3.13 class_get #

struct class * class_get(struct class *cls);

관련함수: class_put

1.13.3.14 class_put #

void class_put(struct class *cls);

관련함수: class_get

1.13.3.15 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);
};

1.13.3.16 CLASS_ATTR #

CLASS_ATTR(name, mode, show, store);

1.13.3.17 class_create_file #

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

1.13.3.18 class_remove_file #

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

1.13.3.19 class_get_devdata #

<linux/device.h>에 선언되어 있다.

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

void * class_get_devdata(struct class_device *dev);

관현 함수 : class_set_devdata

1.13.3.20 class_set_devdata #

<linux/device.h>에 선언되어 있다.

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

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

관련 함수 : class_get_devdata

1.13.3.21 class_devce_register #

<linux/device.h>에 선언되어 있다.

클래스 디바이스를 등록할 때 사용한다.

class_device_register의 함수 원형은 다음과 같다.

int class_device_register(struct class_device *class_dev);

관련함수: class_device_unregister

클래스 디바이스를 등록하기 위해서는 class_device 데이터가 선언되어 있어야 한다. 보통 클래스 디바이스는 다른 데이터 구조체에 포함된 형태로 선언된다.

예를 들어 사용자가 정의한 xxx_device라는 구조체가 있다고 하자. 이 구조체에는 classe_deivce 구조체의 class_dev가 다음과 같이 선언되어 있어야 한다.
static struct xxx_device {
  ...
  struct class_device class_dev;
  ...
};

이제 클래스 디바이스를 등록하기에 앞서 필요한 내용을 채워줄 필요가 있다.
  1. class_device를 위한 메모리 공간 확보 및 초기화
  2. class_device의 class_id 설정
  3. class_device내의 class_data에 사용자 데이터 저장

xxx_device형의 dev가 선언되어 있다고 할 때, 예를 들자면 다음과 같다.
  memset(&dev->class_dev, 0, sizeof(dev->class_dev));
  dev->class_dev.class = &xxx_class;
  strlcpy(dev->class_dev.class_id, xxx_name, KOBJ_NAME_LEN);
  class_set_devdata(&dev->class_dev, xxx_data)

  ret = class_device_register(&dev->class_dev);
  if (unlikely(ret)) {
    ...
    return ERR_PTR(ret);
  }
xxx_class는 클래스 디바이스를 포함하는 클래스이다. xxx_name은 클래스 디바이스의 ID가 되는 문자열이다. xxx_data는 클래스 디바이스 함수 내에서 사용자 데이터를 사용하기 원하는 경우 xxx_data를 클래스 디바이스 내의 class_data에 포인트해 놓을 수 있다.

1.13.3.22 class_device_unregister #

<linux/device.h>에 선언되어 있다.

클래스 디바이스의 등록을 해제한다.

void class_device_unregister(struct class_device *);

관련함수: class_device_register

1.13.3.23 class_device_initialize #

void class_device_initialize(struct class_device *);

1.13.3.24 class_device_add #

int class_device_add(struct class_device *);

1.13.3.25 class_device_del #

void class_device_del(struct class_device *);

1.13.3.26 class_device_rename #

int class_device_rename(struct class_device *, char *);

1.13.3.27 class_device_get #

struct class_device * class_device_get(struct class_device *);

1.13.3.28 class_device_put #

void class_device_put(struct class_device *);

1.13.3.29 struct classs_device_attribute #

<linux/device.h>에 선언되어 있다.

클래스와 클래스 디바이스는 사용자가 직접 볼 수 있는 오브젝트가 아니다. 실질적으로 사용자가 필요한 것은 attribute이다. 클래스 디바이스 attribute는 사용자에게 정보를 보여주거나 사용자가 정보를 저장하기 위하여 사용하게 된다.

이를 위한 기술자는 class_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);
};

1.13.3.30 CLASS_DEVICE_ATTR #

<linux/device.h>에 선언되어 있다.

CLASS_DEVICE_ATTR(_name,_mode,_show,_store)

class_device_attr_##name 의 class_device_attribute 구조체를 생성하는 매크로이다.

1.13.3.31 class_device_create_file #

<linux/device.h>에 선언되어 있다.

클래스 디바이스에 대한 attribute를 등록한다.

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

관련함수: class_device_remove_file

drivers/video/backlight/backlight.c에 있는 내용으로 backlight 클래스 디바이스 아래에 power, brightness, max_brightness 라는 attribute를 추가하는 것에 대해 보면 다음과 같다.
#define DECLARE_ATTR(_name,_mode,_show,_store) \
{ \
  .attr  = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE } \
  .show  = _show, \
  .store = _store, \
}

static struct class_device_attribute bl_class_device_attributes[] = {
  DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
  DECLARE_ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness),
  DECLARE_ATTR(max_brightness, 0644, backlight_show_max_brightness, NULL),
};

int backlight_device *backlight_device_register(...)
{
  ...
  for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
    rc = class_device_create_file(&new_bd->class_dev,
                                  &bl_class_device_attributes[i]);
    if (unlikely(rc)) {
      while (--i >= 0)
        class_device_remove_file(&new_bd->class_dev,
                                 &bl_class_device_attributes[i]);
      ...
    }
  }
  ...
}

1.13.3.32 class_device_remove_file #

<linux/device.h>에 선언되어 있다.

클래스 디바이스의 attribute를 삭제한다.

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

관련함수: class_device_create_file

drivers/video/backlight/backlight.c에 있는 예를 보면 다음과 같다.
  for (i = 0; i < ARRAY_SIZE(bl_calss_device_attributes); i++)
    class_device_remove_file(&bd->class_dev,
                             &bl_class_device_attributes[i]);

1.13.3.33 class_device_create_bin_file #

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

1.13.3.34 class_device_remove_bin_file #

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

1.13.3.35 struct class_interface #

struct class_interface {
  struct list_head node;
  struct class *class;

  int (*add) (struct class_device *)
  void *(remove) (struct class_device *);
};

1.13.3.36 class_interface_register #

int class_interface_register(struct class_interface *);

1.13.3.37 class_interface_unregister #

void class_interface_unregister(struct class_interface *);

1.13.4 class의 사용 예 #

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

1.13.4.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 가 만들어진다.

1.13.4.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 함수를 통해 호출이 된다.

1.13.4.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 디렉토리까지 삭제하게 된다.

1.14 hotplug #

LDM에서는 hotplug event를 처리하기 위한 API를 제공하고 있다. hotplug를 처리하는 핸들러는 사용자 영역의 프로그램으로 /sbin/hotplug 스크립트를 실행하는데, 이때 환경 변수를 통해 적당한 값들을 넘기게 된다. 그 환경 변수로는 다음과 같다.
  • ACTION
  • DEVPATH
  • SUBSYSTEM
  • SEQNUM

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

핫플러그 이벤트를 처리하는 또 다른 방법으로 넷링크 소켓을 이용하는 것이다. 소켓의 경우엔 ACTION@DEVPATH와 같이 각 환경변수에 해당 하는 값이 직접 전달된다.

사용자가 소켓을 여는 간단한 예는 다음과 같다.
#include <linux/netlink.h>

#ifndef NETLINK_KOBJECT_UEVENT
#define NETLINK_KOBJECT_UEVENT   15
#endif

int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_nl snl;
  int retval;

  if (getuid() != 0) {
    fprintf(stderr, "need to be root, exit\n");
    exit(1);
  }

  memset(&snl, 0x00, sizeof(struct sockaddr_nl));
  snl.nl_family = AF_NETLINK;
  snl.nl_pid = getpid();
  snl.nl_groups = 0xffff;

  sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
  if (sock == -1) {
    fprintf(stderr, "error getting socket, exit\n");
    exit(1);
  }

  retval = bind(sock, (struct sockaddr *)&snl,
                sizeof(struct sockaddr_nl));
  if (retval < 0) {
    fprintf(stderr, "bind failed, exit\n");
    goto exit;
  }

  while(1) {
    char buf[1024];
    char object[MAX_OBJECT];
    char *signal;
    char *pos;
    int len;

    len = recv(sock, &buf, sizeof(buf), 0);
    if (len < 0) {
      fprintf(stderr, "error receiving message\n");
      continue;
    }

    buf[len] = '\0';

    /* sending object */
    pos = strchar(buf, '@');
    pos[0] = '\0';

    signal = buf;
    object = &pos[1];

    ...
  }

  ...
exit:
  close(sock);
  exit(1);
}

1.15 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에서 차근 차근 준비가 되어 가고 있으며, 일부 기능은 현재도 사용 가능하다.

1.16 References #


last modified 2006-05-09 11:06:26
EditText|FindPage|DeletePage|LikePages Valid XHTML 1.0! Valid CSS! powered by MoniWiki
0.1880 sec