TIPs

Function Call과 ioctl 사용방법

겨울거북이 2008. 6. 16. 20:26

*** 여기저기서 퍼온 글 두개를 짜집기 해서 - 그냥 두 개를 합쳐서 -_-;;

 

다음에 또 보기 위해 엎어 두자.. ㅋㅋㅋ

 

-------------------------------------------------------------------------

보통 ioctl은 device driver와 맞물려서 사용한다.
device를 리눅스에서 open으로 열게 되면 커널이 적당히 device driver의 함수들을 file descriptor와 연결시켜준다. (정확히는 함수 포인터를 이용한다.) 그러면 ioctl이 정확히 무엇을 하는것인가, 바로 I/O를 control하는 것이다. device driver에서 device를 동작시키기 위한 여러가지 동작이 있을 것이다.

예를 들어 RTC device를 생각하면 클럭을 set하고 on시키고 다쓰고나면 off 시키는 동작을 device driver가 할수 있도록 한다고 생각해보자. 이를 하기 위해선 다음과 같은 방법이 있을것이다.


디바이스를 위한 시스템콜을 만든다. (sys_rtc_on, sys_rtc_off, sys_rtc_set) 유저 프로그램에서 이 시스템 콜을 호출해서 device를 동작시킨다.
memory mapped I/O를 이용하여 device의 특정 메모리 부분을 linear address에 매핑시킨 후 여기에 write를 해서 device에 행동을 전달시킨다.
ioctl을 설정하여 ioctl에 주는 command와 argument를 이용해서 device에 명령을 전달시킨다.
ioctl은 위에서 3번 방법을 이용해서 수행한다. 즉 유저 프로그램에서
fd = open("/dev/rtc", O_RDONLY);
ioctl(fd, SET, arg);
ioctl(fd, on, arg);
ioctl(fd, OFF, arg);
여기서 ioctl을 세번 호출하면서 set, on, off의 명령을 device에 전달하는 것이다.

그렇다면 device driver에서는 어떻게 받겠는가.
static int rct_do_ioctl(cmd, arg, ...)
{
      switch(cmd)
          case SET:
                device set.
          case on:
                device on.
          case OFF:
                device off.
}
위와 같은 형태로 device에 명령을 전달하게 된다. 뭐 자세히는 device에 명령을 전달하는 것을 RISC스타일의 memory mapped I/O로 하던지, Intel 스타일의 I/O 전용 명령어를 사용하던지 할텐데 그건 자세히도 모르고 신경도 쓰지 말자.

뭐 대충은 이러이러하고 좀 더 자세히 알게 되면 부가 설명은 그때 하도록 하자.
[출처] ioctl 사용법|작성자 양군

-------------------------------------------------------------------------

 

System call 은 크게 세가지로 나뉜다.

 

System call 함수 구현.

System call 번호 할당.

User application 구현.

 

 

/커널소스/kernel 디렉토리에 system call을 구현한다.

/* 커널소스가 참조하는 헤더파일의 경로는 다음과 같다 */

/* user application : /usr/include/*.h

    kernel application(system call, module, device driver)

                           : /KERNEL_SRC/include/linux/*.h>

*/

 

eros2.c

 

#include <linux/kernel.h>

#include <linux/errno.h>

 

asmlinkage void sys_eros2(int in)      // 반드시 sys_로 시작해야한다.

// type은 어셈코드와 연동된다고 해서 asmlinkage라고 쓴다.

// 매개변수는 최대 5개 까지밖에 지원을 하지 않는다.

{

    int years;

    years = in;

    printk("\nEROS System Call..........");

    printk("\tYou are \'%d\' years.......");

}

 

 

 

위의 소스가 커널 오브젝트에 포함이 되어야 한다.

그러므로 같은 디렉토리에 있는 Makefile에다가 eros2.c라는 파일이 있음을 알려주어야 한다.

 

1. obj-y = 이 부분에 eros2.o를 추가를 해주어도 되고.

2. obj-$(CONFIG_EROS2_SYSCALL) += eros2.o를 추가해 주어도 된다. 그럴경우.

/커널소스/arch/arm으로 이동.

config.in파일을 수정한다.

'General setup'부분의 바로 밑줄에 아래의 코드를 삽입한다.

bool 'EROS2 System Call Support' CONFIG_EROS2_SYSCALL

 

이제 번호를 할당하는 부분으로 가보자.

/커널소스/include/asm-arm/unistd.h를 보면 여러가지들이 들어있는데..

#define __NR_SYSCALL_BASE 0x900000 이것은 베이스 어드레스 이다.

#define __NR_open은 위에것 +5 이다...

그리고 추가해 주어야 하는 시스템콜은 항상 맨 마지막부분에 적어주어야 한다.

 

#define __NR_eros2  (__NR_SYSCALL_BASE+ 226)

// sys_를 뺀 함수이름을 적고.

 

 

이제 만든 시스템콜을 호출해 주는 부분이 있어야 한다.

/커널소스/arch/arm/kernel/calls.S 이 부분을 수정한다.

역시 제일 아랫부분에 우리가 만든 것을 추가한다.

.long  SYMBOL_NAME(sys_eros2)를 추가한다.

 

 

이제 커널을 컴파일하는 일만 남았다.

/커널소스 디렉토리로 이동.

make menuconfig

General setup부분으로 들어가보면

첫번째 부분에 EROS2 System Call Suppert 부분을 선택을 해주면 된다.

이걸 추가 해 주면 eros2.o파일이 커널소스에 추가가 될 것이다.

make dep

make zImage

cp arch/arm/zImage /tftpboot

보드에 zImage를 넣어준다.

 

 

이제 마지막으로 application을 구현해 주는 부분만 남았다.

 

#include <stdio.h>

#include <linux/unistd.h>

 

_syscall1(void, eros2, int, in);

/* user application : open()

 -> library : open()   

 하지만 우리가 만든 eros2는 이 부분이 없다.

 라이브러리에 없다.

 -> s/w interrupt

 -> system call : sys_open()

그렇기 때문에 위와같이 선언을 해 주어야만 한다.

_syscall0(), _syscall1(), _syscall2(), _syscall3(), _syscall4()

그 안에 들어가는 매개변수는 함수이름과 해당 함수의 타입이 들어가게 된다.

즉 void는 eros2의 타입,

eros2는 함수이름,

int는 매개변수의 타입,

in은 매개변수

*/

 

int main()

{

    int my_years;

    printf("\nInput Your year");

    scanf("%d", &my_years);

 

    eros2(my_years);

 

    return 0;

}

 

이것을 컴파일 한 후. 보드에 올려 실행해보자.. ㅎㅎ

 
 
------------- 으음.. ioctl은 퍼온 데서 작성자라도 가져왔는데,

------------- function call은 어디서 누구에게서 가져왔는지도 모르겠다.

------------- 글을 올려주셨던 모든 분들께 감사한 마음으로.. 잘 쓰겠습니다. ㅎㅎㅎ