디바이스 제어 ioctl() 함수의 역할
저수준 파일 입출력 함수인 ioctl()을 디바이스 파일에 적용시키면 디바이스 파일에 연결된 디바이스 드라이버의 파일 오퍼레이션 구조체의 ioctl 필드에 선언된 함수가 호출된다. ioctl() 함수는 디바이스 파일 이외에는 사용할 수 없는 디바이스 파일 전용 함수이므로 각 디바이스마다 고유하게 선언하여 사용한다. 그래서 read(), write()함수와 같이 정형화된 형태를 기본적으로 유지하지만, 사용 방법은 디바이스마다 모두 다르다. 저수준 입출력 함수인 ioctl() 함수와 디바이스 드라이버에 선언되는 ioctl() 함수의 매개변수 전달 관계는 아래와 같다.
위 그림과 같이 request는 응용 프로그램이 디바이스 드라이버에게 요구하는 명령어고, 이 request에 대입된 값은 디바이스 드라이버의 매개변수 cmd에 그대로 전달된다. 이 값은 디바이스 드라이버 작성자가 임의로 결정하는 사항이다. 하드웨어와 구현되는 디바이스 드라이버에 따라서 값이 전혀 다를 수 있다. 또 동일한 값에 대해 디바이스 드라이버마다 다르게 해석할 수도 있다. 매개변수 argp는 request에 따라서 선언해도 되고 선언하지 않아도 되는 가변 인자 매개변수다. 이 값은 request에 종속되어 디바이스에 전달한 정보를 담은 구조체 변수의 선두 주소를 전달하거나 디바이스 드라이버에서 얻고자 하는 정보를 담아올 수 있는 구조체 변수의 선두 주소를 전달한다. 특별한 경우에는 단순하게 상수값으로 사용할 수도 있다. argp 역시 어떻게 사용될 것인지는 디바이스 드라이버 작성자의 결정에 따라 달라진다.
ioctl() 함수의 특징
read(), write() 함수와 같이 쓰기와 읽기 처리가 가능
하드웨어의 제어나 상태를 얻기 위해 사용
응용 프로그램의 명령에 따라 디바이스 드라이버의 매개변수 해석이 달라짐
ioctl() 함수의 일반적인 형태
ioctl() 동작 개념
응용 프로그램에서 ioctl()함수를 이용하여 하드웨어를 제어하거나 상태를 읽기 위해서는 디바이스 파일을 제어하는 디바이스 드라이버에서 해석 가능한 명령과 구조체를 사용해야 한다. 프로그램을 구현하기 위해 ioctl() 함수를 사용할 때는 공통된 헤더 파일을 사용하게 되는데, 이 헤더 파일에는 ioctl에 전달되는 명령에 대한 선언과 명령을 처리하는 보조적인 정보를 주고받기 위한 구조체가 선언되어 있어야 한다. 디바이스 드라이버의 ioctl() 함수는 파일 오퍼레이션 구조체에 선언된 ioctl()함수가 호출되며, 이때 전달되는 매개변수값은 응용프로그램의 ioctl에서 전달하는 값을 그대로 전달받는다. 응용 프로그램에서 사용하는 매개변수는 응용 프로그램의 request와 argp에 해당하는 값만 전달 받는다. 디바이스 드라이버의 ioctl() 함수는 가장 먼저 전달된 cmd 명령이 유효한가를 확인한다. 전달된 명령이 유효한지 아닌지를 확인하기 위해 _IOC_NR과 _IOC_TYPE이라는 매크로 함수를 사용한다. 전달된 명령이 더 이상 유효하지 않으면, 즉 처리할 수 없는 명령일 경우엔 EINVAL이라는 음수값을 반환한다. 이와는 반대로 전달된 명령어가 정상적인 명령일 경우에는 명령이 읽기를 요구하는지 쓰기를 요구하는지를 검사한다. 이 검사는 _IOC_DIR이라는 매크로를 이용한다. 이 검사를 하는 주된 목적은 사용자 모드에서 메모리의 유효성을 검사하기 위해서이다. 이렇게 기본적인 검사가 끝났다면 switch문을 사용하여 명령을 구분하고 각 명령에 따른 처리를 구현하면 된다.
ioctl()에 전달되는 cmd와 관련 매크로 함수
cmd의 구성
디바이스 드라이버의 ioctl() 함수에 전달되는 매개변수 cmd는 응용 프로그램이 디바이스 드라이버에게 요구한 처리를 구별하기 위한 구별값이다. cmd에는 단순한 구별 숫자 이외에 처리에 도움을 주는 몇 가지 정보를 포함한 형태로 구성된다. cmd의 크기는 32비트로, 비트 구성은 다음과 같다.
매크로 함수
cmd에는 여러 가지 필드가 있다. 리눅스 커널은 이런 필드 형식에 맞춰 cmd 상수값을 만드는 매크로 함수와 cmd 상수값에서 필요한 필드값을 추출하는 매크로 함수를 제공한다.
cmd 명령을 만드는 매크로 함수
_IO : 부가적인 데이터가 없는 명령을 만드는 매크로
_IOR : 디바이스 드라이버에서 데이터를 읽어오기(R) 위한 명령을 만드는 매크로
_IOW : 디바이스 드라이버에서 데이터를 써넣기(W) 위한 명령을 만드는 매크로
_IOWR : 디바이스 드라이버에서 데이터를 읽고(R), 쓰기(W)를 수행하기 위한 명령을 만드는 매크로
이 매크로의 형태는 다음과 같은 형식의 값을 입력하도록 되어 있다.
_IO(매직번호, 구분번호)
_IOR(매직번호, 구분번호, 변수형)
_IOW(매직번호, 구분번호, 변수형)
_IOWR(매직번호, 구분번호, 변수형)
cmd 명령을 해석하는 매크로 함수
_IOC_NR : 구분 번호 필드값을 읽는 매크로
_IOC_TYPE : 매직 번호 필드값을 읽는 매크로
_IOC_SIZE : 데이터의 크기 필드값을 읽는 매크로
_IOC_DIR : 읽기와 쓰기 속성 필드값을 읽는 매크로
매직 번호
매직 번호값의 범위는 0~255 사이다. 보통 영문자 'A'~'Z' 또는 'a'~'z'를 넣는다. 이 매직 번호가 명령을 만들 때 특별한 의미가 있는 것은 아니다. 단지 디바이스 드라이버에서 이 매직 번호를 명령에서 추출하여 자신이 처리하는 매직 번호와 같은지를 비교하여 다르면 처리를 거부한다. 매직 번호는 잘못된 사용을 막는 초보적인 보안 장치다. 디바이스 드라이버에서 매직 번호를 추출하려면 다음과 같은 매크로를 사용한다.
_IOC_TYPE(명령) |
매직 번호는 가급적 다른 디바이스 드라이버와 다르게 하는 것이 좋다. 그렇다고 무조건 다르게 해야 한다는 말은 아니다. 다른 디바이스 드라이버에서 사용한 매직 번호를 사용해도 상관 없다.
구분 번호
구분 번호는 각 명령을 구분하기 위해 사용한다. 보통 0부터 순서대로 붙여나간다. 이 값은 같은 디바이스 드라이버에서는 중복해서 사용해도 된다. 예를 들어, 읽기와 쓰기 각각의 명령에 같은 구분 번호를 사용해도 디바이스 드라이버에서 명령을 구분할 때 switch문을 사용하면서 명령 매개변수인 cmd값을 그대로 사용하기 때문이다. 명령을 만드는 매크로에서 생성되는 값은 여러 필드를 조합하기 때문에 같은 구분 번호라 해도 다른 명령으로 인식할 수 있기 때문이다.
디바이스 드라이버가 명령에서 이 구분 번호를 추출하려면 다음 매크로 함수를 사용한다.
_IOC_NR(명령) |
하지만 switch문에서 각각의 case에 적용되는 값은 일반적으로 명령 자체를 쓴다.
변수형
변수형은 arg 매개변수가 가리키는 데이터의 전달 크기를 지정하는 것을 사용하는데, 숫자를 직접 대입하는 것이 아니고 변수형을 넣는다. 왜냐하면 명령을 만드는 매크로에 크기를 인식하는 sizeof란 컴파일 명령이 포함되어 있기 때문이다. ioctl을 사용할 때는 보통 구조체를 이용하여 구조체 주소를 arg에 넘기기 때문에 구조체형을 넣는 것이 일반적이다.
디바이스 드라이버에 전달된 명령에서 크기값을 추출하려면 다음과 같이 사용한다.
_IOC_SIZE(명령) |
_IO 매크로 함수
이 매크로는 전달되는 매개변수가 없고, 단순히 명령만 전달할 때 사용된다.
예를 들면, 다음과 같이 선언한다.
#define TEST_DRV_RESET _IO('Q', 0)
이때는 응용 프로그램에서 전달되는 arg 매개변수를 생량하거나 0을 대입한다. 이 매크로를 응용 프로그램에서 다음과 같이 사용할 수 있다.
ioctl(dev, TEST_DRV_RESET, 0); 또는 ioctl(dev, TEST_DRV_RESET);
이는 매개변수의 마지막 인자가 가변형 인자기 때문에 가능한 것이다.
이렇게 명령으로만 사용할 경우에는 데이터가 디바이스에 출력될 것인지 입력될 것인지에 대한 구분이 필요 없으므로 디바이스 파일의 열기 옵션에 대한 처리를 디바이스 드라이버에서 수행할 필요가 없다.
_IOR 매크로 함수
이 함수는 디바이스 드라이버에서 데이터를 읽어오는 명령을 만들 때 사용한다. 예를 들어, 다음과 같이 선언한다.
#define TEST_DRV_READ _IOR('Q', 1, int)
이는 디바이스에서 응용 프로그램이 읽어올 데이터의 크기가 int크기 만큼이라는 의미다. 디바이스 드라이버에 전달된 cmd 명령값에서 읽기용인지 쓰기용인지를 알아내기 위해 사용하는 매크로는 다음과 같다.
_IOC_DIR(명령) |
이 매크로를 수행했을 때 반환되는 값의 종류는 다음과 같다.
_IOC_NONE : 속성이 없다.
_IOC_READ : 읽기 속성이다.
_IOC_WRITE : 쓰기 속성이다.
_IOC_READ|_IOC_WRITE : 읽기, 쓰기 속성이다.
일반적으로 이 명령을 사용할 때, 응용 프로그램에서 ioctl()함수의 arg매개변수값은 디바이스 드라이버에서 데이터를 읽기 위한 데이터 버터(구조체)의 주소를 지정한다.
_IOW 매크로 함수
디바이스에 데이터를 쓸 명령을 만들 때 사용한다. 그 외의 내용은 _IOR과 같다. 일반적으로 이 명령을 사용할 때 ioctl()의 arg 매개변수값은 디바이스 드라이버에 데이터를 쓰기 위한 버터(구조체)의 주소를 지정한다.
_IOWR매크로 함수
디바이스에 데이터를 쓰고 읽기 위한 명령을 만들 때 사용한다. 그 외의 내용은 _IOR과 같다. 일반적으로 이 명령을 사용할 때 ioctl()의 arg매개변수값은 디바이스 드라이버에서 데이터를 쓰고, 읽기 위한 데이터 버퍼(구조체)의 주소를 지정한다.
출처 : http://blog.naver.com/luis8282?Redirect=Log&logNo=20086600288