[펌] http://blog.naver.com/wassupnari/100105102485
linux kernel과 userspace간의 데이터 교환은 언제나 큰 이슈인데, 대부분 read/write system call로 데이터를 넘겨주고 받거나, 혹은 ioctl을 이용해 단순한 값등을 넘기고 device만의 특정 커맨드를 넘겨줄 수 있다. 더불어 procfs가 있는데 이것은 kernel에 보다 직접적으로 접근할 수 있는 방법으로써, kernel 혹은 driver단에서 직접 /proc 에 특정 node를 생성해서 userspace와 직접적으로 data 교환이 가능하다. 현재는, 이것이 여기에서 말하려 하는 sysfs로 발전되어 가는 과정이라고 하는데, 솔직히 그 히스토리는 관심이 없고. 워낙 역사공부를 좋아라 하는 편이 아니라. :)
2가지 방법이 있는데, 사용법만 간추려 정리해보면 다음과 같다.
1. device 구조체를 직접 이용해 생성하는 방법
주로 shell상에서 echo등을 이용해 간단한 값을 넣거나, 혹은 cat등으로 간단한 값을 읽어올때 사용하기 편하다.
- _name : device 이름. 접두어로 dev_attr_ 이 붙는다.
- _mode : 권한. linux나 unix등에서 사용하는 권한설정 숫자값을 그대로 사용.
4, 2, 1의 조합으로 이루어지는 그 숫자 말이다. 궁금한 분은 chmod를 뒤져볼것.
- _show : kernel에 userspace에게 값을 보여주는(넘겨주는) func 포인터
- _store : userspace에서 kernel로 값을 보여주는(넘겨주는) func 포인터
위 세가지로, struct device에 지정된 /sys 아래의 특정 device 디렉토리 아래에 sysfs node를 생성할 수 있다.
다음은 _show와 _store에 선언된 특정 func의 prototype 및 pseude code이다.
return
값으로 둘다 count를 넘겨주는데, xxxx_store는 kernel주체로 동작되기 때문에, userspace쪽에서 넘겨주는
값의 길이를 알 수 없으므로 count인자를 받아야 한다. 대신, _store는 값을 받아오는 쪽이라, 굳이
userspace쪽으로 넘겨줄 값을 생각해 줄 필요는 없다.
반대로, xxxx_show는 kernel주체로 동작되더라도, userspace쪽에서 보여주는 함수이니 count함수는 userspace쪽에서나 필요하다. 대신, 꼭 code 내부에서 buf에 write한 count값을 고려해서 return으로 넘겨주어야 한다.
2. 간접적으로 kobj 구조체를 이용해 생성하는 방법
kobj 를 이용하면, binary file등의 덩치큰 파일들을 직접 쓰고 읽을 수 있다. (솔직히 읽는 것은 문제가 있을 법도 한데, 테스트를 못해봐서 -.-) 따라서, device_attribute 구조체 대신, bin_attribute 구조체를 사용한다. 기본적 node 생성방법은 다음과 같다.
device_attribute 와 마찬가지로, 권한 mode, read/write/mmap 함수의 func 포인터를 지정해준다. name은 그냥 지정해줄 수 있고(device_attribute에도 있는지는 모르겠음. 찾아봐야함.) 가장 크게 다른점은, buffring을 위해 한번에 가능한 MAX size를 지정해줘야 한다는 것이다. binary data이므로 용량이 큰 파일들을 주로 쓰기 편하게(device에 올릴 firmware 같은 것들) 만들어놓은 것인듯.
read와 write함수의 prototype은 다음과 같다.
여 기서 중요한 것은, buffering되는 MAX 값에 따라 여러번 위 함수들이 호출된다는 것인데, 특히 write에서 쓰고자 하는 file이나 data가 MAX_BUF보다 크게 되면 그 배수만큼 여러번 call된다. 따라서 그에 맞는 처리를 해주어야 한다. 그 부분은 알아서들 하시길 바라며 ... :-)
3. data 교환을 위한 내부의 local 변수 선언
device_create_file() 을 이용하든 sysfs_create_bin_file()을 이용하든, 두 경우 모두 device의 struct device * 구조체 포인터만 알고 있으면 쉽게 적용 가능하다. device_create_file()의 경우 인자로 직접 struct device * 를 받으니, device_create_file()을 call할때 직접 그 포인터를 넘겨주면 된다, sysfs_create_bin_file()의 경우는 인자로 struct kobject *를 받는데, 이것은 &(struct device).kobj 값을 넘겨주면 된다.
또 하나, device 구조체를 이용하는 방법에서는 read/write 함수에서 struct device * 를 인자로 받기 때문에, 그 device 구조체를 보유하고 있는 상위 buf구조체나 platformdata등을 구하면 된다. 일례로, i2c client framework의 경우, 다음과 같이 i2c_client 구조체를 얻어올 수 있다.
마
찬가지로, sysfs_create_bin_file()의 경우에는 인자로 struct kobject *를 받는다. 따라서 우선,
struct device *를 구하고, 그것을이용해 다시 struct i2c_client *를 얻는다.
이런식으로 응용하시면 되겠다.
linux kernel과 userspace간의 데이터 교환은 언제나 큰 이슈인데, 대부분 read/write system call로 데이터를 넘겨주고 받거나, 혹은 ioctl을 이용해 단순한 값등을 넘기고 device만의 특정 커맨드를 넘겨줄 수 있다. 더불어 procfs가 있는데 이것은 kernel에 보다 직접적으로 접근할 수 있는 방법으로써, kernel 혹은 driver단에서 직접 /proc 에 특정 node를 생성해서 userspace와 직접적으로 data 교환이 가능하다. 현재는, 이것이 여기에서 말하려 하는 sysfs로 발전되어 가는 과정이라고 하는데, 솔직히 그 히스토리는 관심이 없고. 워낙 역사공부를 좋아라 하는 편이 아니라. :)
2가지 방법이 있는데, 사용법만 간추려 정리해보면 다음과 같다.
1. device 구조체를 직접 이용해 생성하는 방법
주로 shell상에서 echo등을 이용해 간단한 값을 넣거나, 혹은 cat등으로 간단한 값을 읽어올때 사용하기 편하다.
- device_create_file(struct device *, struct device_attribute *)
- device_remove_file(struct device *, struct device_attribute *)
- static DEVICE_ATTR(_name, _mode, _show, _store)
- _mode : 권한. linux나 unix등에서 사용하는 권한설정 숫자값을 그대로 사용.
4, 2, 1의 조합으로 이루어지는 그 숫자 말이다. 궁금한 분은 chmod를 뒤져볼것.
- _show : kernel에 userspace에게 값을 보여주는(넘겨주는) func 포인터
- _store : userspace에서 kernel로 값을 보여주는(넘겨주는) func 포인터
위 세가지로, struct device에 지정된 /sys 아래의 특정 device 디렉토리 아래에 sysfs node를 생성할 수 있다.
다음은 _show와 _store에 선언된 특정 func의 prototype 및 pseude code이다.
- static ssize_t xxxxx_store(struct device *, struct device_attribute *,
- const char *buf, size_t count)
- {
- u8 data;
- scanf(buf, fmt, &data);
- return count;
- }
- static ssize_t xxxxx_show(struct device *, struct device_attribute *,
- char *buf)
- {
- int count;
- u8 data;
- count += sprintf(buf, fmt, data);
- return count;
- }
반대로, xxxx_show는 kernel주체로 동작되더라도, userspace쪽에서 보여주는 함수이니 count함수는 userspace쪽에서나 필요하다. 대신, 꼭 code 내부에서 buf에 write한 count값을 고려해서 return으로 넘겨주어야 한다.
2. 간접적으로 kobj 구조체를 이용해 생성하는 방법
kobj 를 이용하면, binary file등의 덩치큰 파일들을 직접 쓰고 읽을 수 있다. (솔직히 읽는 것은 문제가 있을 법도 한데, 테스트를 못해봐서 -.-) 따라서, device_attribute 구조체 대신, bin_attribute 구조체를 사용한다. 기본적 node 생성방법은 다음과 같다.
- sysfs_create_bin_file(struct kobject *, struct bin_attribute *)
- sysfs_remove_bin_file(struct kobject *, struct bin_attribute *)
- static struct bin_attribute xxxx_attr = {
- .attr = {
- .name = "xxxx",
- .mode = S_IRWXUGO,
- },
- .size = MAX_BUF,
- .read = xxx_sysfs_read,
- .write = xxx_sysfs_write,
- .mmap = xxx_sysfs_mmap,
- }
device_attribute 와 마찬가지로, 권한 mode, read/write/mmap 함수의 func 포인터를 지정해준다. name은 그냥 지정해줄 수 있고(device_attribute에도 있는지는 모르겠음. 찾아봐야함.) 가장 크게 다른점은, buffring을 위해 한번에 가능한 MAX size를 지정해줘야 한다는 것이다. binary data이므로 용량이 큰 파일들을 주로 쓰기 편하게(device에 올릴 firmware 같은 것들) 만들어놓은 것인듯.
read와 write함수의 prototype은 다음과 같다.
- static ssize_t xxxx_sysfs_read(struct kobject *, struct bin_attribute *,
- char *buf, loff_t off, size_t size)
- {
- int count;
- u8 data;
- if (off >= MAX_BUF)
- return 0;
- if (off + size > PAGE_SIZE)
- return 0;
- count += sprintf(buf, fmt, data);
- return count;
- }
- static ssize_t xxxx_sysfs_write(struct kobject *, struct bin_attribute *,
- char *buf, loff_t off, size_t size)
- {
- if (off == 0) {
- /* set the values, for the first time */
- }
- return size;
- }
- static ssize_t xxxx_sysfs_mmap(struct kobject *, struct bin_attribute *,
- struct vm_area_struct *vma)
- {
- /* ? */
- }
여 기서 중요한 것은, buffering되는 MAX 값에 따라 여러번 위 함수들이 호출된다는 것인데, 특히 write에서 쓰고자 하는 file이나 data가 MAX_BUF보다 크게 되면 그 배수만큼 여러번 call된다. 따라서 그에 맞는 처리를 해주어야 한다. 그 부분은 알아서들 하시길 바라며 ... :-)
3. data 교환을 위한 내부의 local 변수 선언
device_create_file() 을 이용하든 sysfs_create_bin_file()을 이용하든, 두 경우 모두 device의 struct device * 구조체 포인터만 알고 있으면 쉽게 적용 가능하다. device_create_file()의 경우 인자로 직접 struct device * 를 받으니, device_create_file()을 call할때 직접 그 포인터를 넘겨주면 된다, sysfs_create_bin_file()의 경우는 인자로 struct kobject *를 받는데, 이것은 &(struct device).kobj 값을 넘겨주면 된다.
또 하나, device 구조체를 이용하는 방법에서는 read/write 함수에서 struct device * 를 인자로 받기 때문에, 그 device 구조체를 보유하고 있는 상위 buf구조체나 platformdata등을 구하면 된다. 일례로, i2c client framework의 경우, 다음과 같이 i2c_client 구조체를 얻어올 수 있다.
- static ssize_t xxxxx_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct i2c_clinet *c = container_of(dev, struct i2c_client, dev);
- .............
- static ssize_t xxxx_sysfs_read(struct kobject *, struct bin_attribute *,
- char *buf, loff_t off, size_t size)
- {
- struct device *dev = container_of(kobj, struct device , kobj);
- struct i2c_client *c = container_of(dev, struct i2c_client, dev);
- ..................
이런식으로 응용하시면 되겠다.
출처 : http://suchman.tistory.com/
'Kernel & Uboot' 카테고리의 다른 글
linux logo 변경하기. (0) | 2012.10.08 |
---|---|
u-boot home page (0) | 2012.08.17 |
i2c probe함수 호출 및 dev i2c addr등록 방법 (0) | 2012.07.15 |
Video for Linux Two API Specification (0) | 2012.05.29 |
S5PC100 FIMC와 S3C6410 FIMC와의 차이 (0) | 2011.03.03 |