노무현 대통령 배너


2007. 7. 13. 10:47

터치스크린

터치스크린
project pool/ZBOS | 2006/09/19 05:29

터치스크린과 리눅스 디바이스 드라이버


유영창| FA 리눅스(주)


터치스크린은 마우스와 유사한 포인터 입력 장치이지만 똑같은 방식으로 동작하는 장치는 아니다. 이번 컬럼에서는 개발자가 임베디드 리눅스에서 터치스크린을 다루어야 하는 경우 알아야 할 디바이스 드라이버와 관련한 내용을 커널 2.6 기준으로 살펴보고자 한다.


PDA장치는 필수적으로 GUI 기능이 구현되어야 한다. PDA 장비에 가장 많이 사용되는 입력 장치로는 터치스크린이 있는데, PC 시스템이라면 마우스나 키보드를 사용하기 때문에 공장자동화에 쓰이는 모니터링 시스템이나, 제어장치나 물류 시스템에 사용되는 POS 시스템과 같은 특수한 경우를 제외하면 일반인이 보기 힘든 것이 터치스크린이다. 반면에 작은 그래픽 화면에서 동작하는 조건에서 키보드와 같은 부가적인 입력 장치를 달기 곤란한 PDA류의 임베디드 제품들에서는 터치스크린은 흔히 볼 수 있는 입력 장치이다.


터치스크린은 기본적으로 절대 좌표계를 사용한다는 점과 좌표 입력이 발생하려면 접촉되어야 한다는 점이 마우스 입력 장치와 가장 큰 차이점이다. 임베디드 리눅스는 PC 리눅스와 동일한 시스템을 사용하기 때문에 터치스크린 역시 마우스와 같은 장치로 봐야 하고 처리돼야 하지만 그 입력 처리 방식의 차이점 때문에 조금 다른 관점에서 처리되어야 한다. 특히 디바이스 드라이버 입장에서는 하드웨어 구조가 다르기 때문에 기존 마우스 처리와 다른 형태를 가지고 있다.


터치스크린


PDA와 같은 휴대용 장치에 사용되는 터치스크린은 사용자의 입력을 좌표 값으로 바꿔주는 시스템이다. 일반적으로 터치스크린은 LCD와 같은 화면표시 장치와 연동돼서 움직인다. 입력 방식은 손으로 직접 위치를 지정하거나 스타일러스 펜과 같은 전용 입력 도구로 지정한다. 화면 크기가 작은 PDA 시스템은 손가락으로 입력이 가능하지만 접촉 범위가 커지므로 스타일러스 펜을 제공하여 입력 위치를 세밀하게 처리하고 있다. 스타일러스 펜은 끝이 둥그렇게 처리되었는데 이는 터치스크린의 손상을 방지하기 위한 목적도 있다. 터치스크린에 의해서 입력된 내용은 내부 장치에 의해서 좌표로 변환된다. 이렇게 변환된 좌표는 LCD상에 커서로 표시하여 사용자가 입력된 위치를 인식하도록 도와준다. 하지만 PDA의 경우에는 입력 위치가 곧 표출 위치이기 때문에 입력된 위치를 커서 형태로 굳이 표현하지 않는 경우가 많다. 어떤 시스템은 터치스크린의 크기가 LCD 크기를 벗어나서 필기체 입력을 처리하도록 도와주는 영역을 두기도 한다.


터치스크린의 종류


터치스크린의 종류는 매우 다양하다. 보통 처리 방식에 따라서 다음과 같은 방식으로 나눠진다.


매트릭스 스위치(Matrix Switch) 방식
- 광센서(적외선) 매트릭스 방식
- 도전 필름 방식 - 저항막 방식
- 용량변화 방식 - 정전 용량 방식
- 금속세선매립 방식

아날로그 방식
- 도전 필름 방식(Analog)
- 하중분압(압력 센서) 방식
- 표면파(초음파) 반사 방식
- 금속세선매립 방식(감압 방식)


이중 PDA와 같은 휴대용 기기에 가장 많이 사용되는 방식은 저항막 방식이고 POS와 같은 시스템에서는 정전 용량 방식도 사용된다. 이 연재에서는 주로 저항막 방식에 대해서 설명할 것이다. 참고로 정전 용량 방식 역시 간략하게 소개하겠다.


저항막 방식

유리나 투명한 플라스틱 판 위에 저항성분의 물질을 입히고 그 위에 폴리에스틸렌 필름을 덮어씌운 형태로 되어 있으며, 두 면이 서로 닿지 않도록 일정한 간격으로 절연층(Dot Spacer)이 설치되어 있다.


동작 상태에서는 저항막의 양단에서 일정한 전류를 흘려주면 저항막이 저항 성분을 갖는 저항체와 같이 작용하기 때문에 양단에 전압이 걸리게 된다. 손으로 접촉을 하게 되면 위쪽 표면의 폴리에스틸렌 필름이 휘어지면서 두 면이 접속하게 된다. 이때 두 면의 저항 성분 때문에 저항의 병렬접속과 같은 형태가 되고 저항 값의 변화가 일어나게 된다. 이때 양단에 흐르는 전류에 의하여 전압의 변화도 일어나게 되는데, 바로 이러한 전압의 변화 정도로써 접촉된 손의 위치를 알 수 있다. 이 방식은 다음과 같은 특징을 가진다.


◆ 원리의 특성상 두 면이 닿아야 인식된다.
◆ 볼펜이나 기타 물질에 의한 압력으로도 인식된다.
◆ 뾰쪽한 물체에 의한 손상이나 사용빈도에 따라 표면에 긁힘이 날 수 있다.
◆ 수명이 거의 영구적이다(한 점당 200만 터치 ; 일반적으로 약 10년 간 사용)
◆ 터치시 반응속도가 빠르다(초당 약 150번 이상)
◆ 오차율이 적다(2%).
◆ 투명도가 좋음(폴리에스틸렌 필림의 종류에 따라 88~92%의 투과율)


정전 용량 방식

유리 또는 플라스틱, 필름과 같은 양면에 투명한 특수전도성 금속(TAO)을 코팅되어지는 구성을 갖는다.


스크린의 네 귀퉁이에 전압을 걸어준다. 구석에서 발생되는 고주파가 센서 전면에 퍼지게 된다. 이때 손가락으로 스크린을 접촉하게 되면 수신부에서는 변형된 파형이 감지된다. 이를 가지고 컨트롤러에서는 위치를 계산하게 된다. 이 방식은 다음과 같은 특징을 가진다.


◆ 미세한 정전압에도 반응하므로 살짝만 접촉되어도 감지한다.
◆ 높은 분해 능력을 가진다(1024×1024).
◆ 강화 처리된 유리에 특수 금속 코팅을 했기 때문에 견고성이 좋다.
◆ 수명이 길다(1 포인트 당 2000만 터치).
◆ 빛 투과율이 높아 원 화상의 색상을 그대로 살릴 수 있다.
◆ 터치 시 반응속도가 매우 빠르다(초당 약 270번 이상).
◆ 오차율이 매우 적어 정확하다(오차율 : 1%).


<그림 1> 저항막 방식


<그림 2> 정전 용량 방식


<그림 3> 터치스크린 라인 층


<그림 4> 터치스크린의 저항 특성


<그림 5> 접촉 검출 구조


<그림 6> 등가 회로


터치스크린 연결 방식

터치스크린과 컨트롤러에 입력되는 라인 수에 따라 크게 4선식과 5선식으로 나누어진다. 4선식은 저항막 방식에서 사용되는 업게 표준 방식이다. 5선식은 4선식의 신호에 사용자 입력의 압력 감도를 받을 수 있는 방식으로 그리 흔하게 사용되는 방식은 아니다.


POS 시스템의 경우라면 터치스크린을 연결하는 경우 RS-232나 USB와 같은 버스 방식으로 연결될 수 있다. 하지만 이 경우는 업계마다 그 연결 방식이 다르고 터치스크린을 처리하는 컨트롤러가 독립적으로 존재하는 경우이기 때문에 이번 연재에서는 다루지 않는다.


터치스크린의 하드웨어 구성과 동작 원리


저항식 터치스크린은 일반적으로 4선식으로 구성되어 있다. 이 4선은 터치스크린의 층에 연결되어 <그림 3>과 같은 신호라인을 갖는다.


업체마다 각 층의 구성은 다르지만 보편적으로 가장 상층이 X축 검출을 위한 층이고, 그 다음은 ITO라고 불리는 절연층, 그 다음은 Y축 검출을 위한 층, 마지막으로 터치스크린 지지를 위한 유리나 플라스틱의 고형물이 된다. 각 층은 저항 피막으로 코팅되어 있으므로 논리적으로는 하나의 저항으로 봐야 한다.


ITO 절연층은 터치가 되는 순간에 상층의 X축 검출을 위한 코팅 층과 바로 밑의 Y축 검출을 위한 코딩층이 연결된다. 만약 눌린 순간을 검출하는 것을 그림으로 표현하면 <그림 5>와 같은 형식이 된다. 이를 회로적으로 검출하기 위한 회로상 표현은 <그림 6>이 된다. 즉 R2에 걸리는 전압을 측정하고 이 전압과 인가된 VCC의 비율이 접촉된 X의 위치가 되는 것이다. Y의 위치를 측정하기 위해서는 이와 반대로 Y(+)에 VCC를 인가하고 Y(-)에 GND를 연결하고 X(+)에서 측정된 전압 값을 측정하면 된다.


일반적으로 ADC 컨트롤러의 분해능이 측정 정밀도를 결정하는데 입력 위치의 검출을 위해서 사용되는 ADC는 축차 비교형을 사용하기 때문에 일정한 주기를 가져야 한다.


소프트웨어적으로 이런 저항식 터치스크린의 컨트롤러를 제어할 경우라면 다음과 같은 순서가 진행된다.


1. X(+)에 VCC가 인가하고 X(-)에 GND 단자가 연결되도록 제어한다.
2. ADC 입력을 Y(+)에 연결하고 Y(-)에는 아무런 연결이 되지 않도록 한다.
3. ADC의 전압 측정을 시작하도록 한다.
4. ADC의 전압 측정이 종료되면 해당 전압에 해당하는 디지털 값을 읽고 이 값을 X 좌표의 측정 값으로 저장한다.
5. Y(+)에 VCC가 인가되고 Y(-)에 GND 단자가 연결되도록 제어한다.
6. ADC 입력을 X(+)에 연결하고 X(-)에는 아무런 연결이 되지 않도록 한다.
7. ADC의 전압 측정을 시작하도록 한다.
8. ADC의 전압 측정이 종료되면 해당 전압에 해당하는 디지털 값을 읽고 이 값을 Y 좌표의 측정 값으로 저장한다.


이와 같은 과정은 터치스크린 컨트롤러에 따라서 자동으로 해주는 경우가 있는데 이때 역시 원리적으로 이와 같은 과정이 처리되는 것을 이해해야 컨트롤러 제어에 대한 프로그램 디버깅을 쉽게 할 수 있다. 보통 터치스크린이 눌린 상태가 되면 컨트롤러는 인터럽트를 발생하게 되는데 이것은 X 값이나 Y 값이 일정 전압 이하가 되는 상태를 검출하여 만들어진다.


터치스크린의 입력을 처리하기 위해서 터치 컨트롤러는 보통 일정한 주기로 이와 같은 처리를 반복하게 한다. 정확한 입력 위치를 얻기 위해서 샘플링 값을 평균 내어 처리하기 때문이다.


리눅스와 터치스크린


터치스크린을 리눅스 시스템에서 다루기 위해서는 리눅스에서 입력 장치를 어떻게 다루는지를 알아야 한다. 터치스크린은 PC 시스템에서 그리 흔하지 않은 시스템이기 때문에 표준적인 처리 방식은 초기 설계에 반영되어 있지 않았다. 그러나 리눅스 개발자들은 필요에 의해서 터치스크린 입력을 기존의 마우스 입력을 이용하여 처리하였다. 다행히도 마우스 입력 장치용 디바이스 드라이버는 디지타이저와 같은 타블렛 처리 장치들을 처리하기 위해서 절대 좌표를 처리하도록 하고 있었다.


이 특성을 이용하여 개발자들은 마우스를 에뮬레이션하도록 처리하여 터치스크린을 처리하는 방법을 개발하였다. 이것은 리눅스 디바이스 드라이버가 정식으로 터치스크린을 지원하지 않는다는 것을 의미하기도 한다. 이런 점은 커널 2.6에서 개선되었다. 임베디드 시스템에서 리눅스가 사용되고 또한 USB와 같은 다양한 입력장치를 지원하기 위해서 리눅스의 입력 시스템을 처리해야 하는 사용자 환경 변화가 발생했고 이를 해결하는 과정에서 터치스크린 문제도 자연적으로 해결되었다. 우선적으로 리눅스에서 터치스크린을 응용 프로그램에서 사용할 수 있도록 지원하는 구조를 그림으로 살펴보자.


<그림 7>에서 보듯 리눅스가 터치스크린을 처리하기 위해서는 크게 디바이스 드라이버를 구현하는 커널부와 터치 시스템을 이용하는 응용 프로그램 처리부로 나눌 수 있다. 응용 프로그램 처리부는 터치를 포함한 다양한 입력 장치를 다루기 위한 응용 프로그램의 드라이버 부분과 실제로 응용하는 프로그램부분으로 나눌 수 있다.


하드웨어

터치스크린에서 하드웨어는 터치 장치와 터치를 제어하고 좌표 값을 ADC(아날로그-디지털 변환 컨트롤러)를 이용하여 실제 눌린 위치(X, Y)와 눌림 상태를 얻어오는 컨트롤러를 포함한다. 이 컨트롤러는 커널 디바이스 드라이버 층에서 해석되어 응용 프로그램에 전달된다.


커널 디바이스 드라이버

터치 컨트롤러는 하드웨어 장치이기 때문에 리눅스 시스템에서는 응용 프로그램이 직접적으로 접근이 불가능하다. 응용 프로그램이 하드웨어를 제어하기 위해서 커널은 디바이스 드라이버를 제공하여야 하고 이 디바이스 드라이버를 커널이 제공하는 디바이스 파일 시스템을 이용하여 접근해야 한다. 터치스크린과 관련한 디바이스 드라이버는 커널 2.6부터 INPUT 디바이스 드라이버로 통합되어졌다. 터치 디바이스 드라이버를 사용하기 위해서는 리눅스 디바이스 드라이버의 한 형태인 INPUT 디바이스 드라이버의 이해가 필요하고 부가적으로 이벤트 핸들러 디바이스인 mouse와 event 및 tsdev 디바이스 드라이버에 대한 이해가 필요하다.


응용 프로그램

GUI를 구현하는 응용 프로그램으로 가장 대표적인 것을 들라면 X가 있다. 임베디드 시스템은 흔히 TinyX를 사용하는데 방대한 X의 기능 중 필요한 기능만을 구현한 간단한(?) X이다. 이 X는 여러 가지 입력 장치를 처리하기 위해서 하부 구조에 입력 모듈을 구분하여 드라이버처럼 다루고 있다. 이 드라이버 중 하나에 터치스크린을 처리하도록 하는 드라이버가 필요하다. 여기서 드라이버라는 의미는 라이브러리라는 의미도 되고 모듈의 의미도 된다.


<그림 7> 터치 시스템을 처리하기 위한 리눅스 구조


임베디드 리눅스 시스템에서 사용되는 GUI 라이브러리 중에는 QT도 포함된다. 이 QT를 우리는 GNOME과 대치되는 X의 상층부에 구현되는 매니저 시스템으로 알고 있지만 임베디드 리눅스에서는 QT 자체가 그래픽 라이브러리의 묶음 형태를 가지고 있기 때문에 X를 사용하지 않고도 그래픽 처리가 가능하다. QT 역시 X와 유사하게 입력 장치를 위한 드라이버 모듈이 필요하다. 윈도우의 API와 유사한 함수나 아주 작은 X를 구현하여 GUI를 구현하고 싶다면 마이크로윈도우(MicroWindow or NanoX)라는 라이브러리를 고려해보는 것도 좋다. 이 마이크로윈도우 역시 가장 하부 단에는 입력 드라이버를 만들어줘야 한다.


응용 프로그램에서 터치를 이용하기 위해서는 GUI 라이브러리나 시스템을 이용하게 된다. 이 GUI 라이브러리는 다양한 입력 장치를 모두 처리하기 위해서 각각의 라이브러리나 시스템별로 자신만의 독특한 규칙을 가지고 있다. 모든 라이브러리나 시스템에 대해서 이해할 수는 없지만 대부분의 구성이 유사하기 때문에 한두 가지만 이해하고 나면 나머지는 필요에 따라서 금방 파악할 수 있을 것이다. 이 글에서는 아쉽게도 지면관계상 이 부분의 설명이 빠지게 되었다. 독자에게 양해를 구한다.


INPUT 디바이스 드라이버


리눅스 시스템 커널에서 터치스크린을 다루는 방식을 이해하기 위해서 가장 먼저 입력(INPUT) 디바이스 드라이버가 어떻게 구성되며, 어떤 특성이 있는가에 대하여 이해해보는 것은 무척 중요하다. 임베디드 시스템에서 터치스크린용 디바이스 드라이버를 만들어야 하는 개발자라면 반드시 이 부분의 이해가 필요하다. 이런 이해 없이 디바이스 드라이버를 제작하면 절름발이 드라이버를 만들 공산이 크다. 그래서 여기서는 커널 2.6을 중심으로 입력 디바이스 드라이버를 설명하고자 한다.


PC 시스템에서 사용자의 입력을 처리하는 입력 장치는 크게 키보드, 마우스, 조이스틱, 터치스크린으로 나누어 볼 수 있다. 초기 PC 시스템은 이 네 가지 입력장치에 해당하는 장치가 많지 않았기 때문에 표준적인 처리 방법을 따로 구현하지 않았다. 필요에 의해서 그때그때 만들어져 갔다는 것이 더 맞을지도 모른다. 키보드라면 IBM 키보드였고 마우스라면 마이크로소프트 마우스, 조이스틱은 우리가 흔히 아는 조이스틱 정도였다. 그런데 PC 시스템이 발달하고 USB라는 버스가 생기면서 입력 장치는 여러 가지 형태로 구현되기 시작했다.


<그림 8> INPUT 디바이스 드라이버 구성과 응용 프로그램


더구나 터치스크린을 사용하거나 타블렛 또는 디지타이저와 같은 입력장치가 등장하면서 리눅스 디바이스 드라이버 개발자들은 그때그때 필요한 입력 장치를 만들어갔다. 이것은 나중에 GUI 응용 프로그램이 각각의 입력장치에 대한 처리 루틴을 따로따로 만들어 가는 문제를 야기하고 USB 입력 장치에 대응하는 것이 점점 힘들어지는 요소가 되었다. 그나마 다행인 것은 드라이버 제작자들은 기존의 디바이스 드라이버의 입출력 처리 규칙을 지키고자 노력해서 큰 혼란이 생기지 않았다는 정도이다.


리눅스 커널 개발자들은 이런 다양한 입력 장치를 통합하고자 노력했고 그 결과물로 입력 디바이스 드라이버라는 형태로 발전한다. 이것은 기존의 PS/2나 시리얼을 이용하는 단순한 입력 장치뿐만 아니라 USB나 기타 버스를 이용하는 입력 장치에도 대응될 수 있도록 디바이스 드라이버 구성을 표준화한 것이다. INPUT 디바이스 드라이버가 만들어진 가장 큰 공로자는 역시 USB 버스의 출연이다.


입력(INPUT) 디바이스 드라이버의 역할은 크게 두 가지로 볼 수 있다. 첫 번째는 시스템에 연결된 입력 장치의 정보를 파악하여 응용 프로그램이 대응할 수 있도록 한 것과 이벤트 핸들러 디바이스 드라이버들의 등록 처리 방식에 대한 표준적인 절차를 마련한다는 것이다. 입력 디바이스 드라이버는 INPUT 디바이스 드라이버를 중심으로 다음과 같은 이벤트 핸들러 디바이스 드라이버로 구성된다.


◆ 키보드 디바이스 드라이버(kbd)
◆ 마우스 디바이스 드라이버(mousedev,mice)
◆ 조이스틱 디바이스 드라이버(joydev)
◆ 이벤트 디바이스 드라이버(evdev)
◆ 터치스크린 디바이스 드라이버(tsdev)


이중 이벤트 디바이스 드라이버와 터치스크린 디바이스 드라이버는 현재 진행형이다. 즉 아직도 개발이 완료된 것이 아니고 계속 개발 중이라는 것이다. 그렇다고 불안전한 것이라는 의미는 아니다. 현재로도 충분히 쓸만하지만 개발자가 아직 종료되었다고 선언하지 않았다는 의미이다.


INPUT 디바이스 드라이버가 입력 디바이스 드라이버를 총체적으로 관리하는 디바이스 드라이버라면 앞서 열거한 디바이스 드라이버는 응용 프로그램에서 실제로 접근하여 입력 값을 읽어 올 수 있는 디바이스 드라이버이다. 우리가 터치스크린 디바이스 드라이버를 만든다면 반드시 INPUT 디바이스 드라이버에서 제공하는 API를 이용하여 등록해야 하고 앞서 열거한 이벤트 핸들러 디바이스 드라이버 중 하나 이상에 소속되도록 해야 한다. 이것을 도식적으로 표현하면 <그림 8>과 같다.


입력 디바이스 드라이버는 앞서 언급했듯이 크게 두 가지 기능을 제공한다. 첫 번째는 등록된 이벤트 핸들러의 정보와 입력 디바이스 정보를 제공하며, 두 번째는 이벤트 핸들러 디바이스 드라이버를 관리하고 실제로 동작되는 입력 디바이스 드라이버를 이벤트 핸들러 디바이스와 연결하는 역할과 전달된 입력 상태를 각각의 이벤트 핸들러에 전달하는 역활을 담당한다.


입력 디바이스 드라이버가 등록된 이벤트 핸들러 정보와 디바이스 정보를 표현하는 방법은 proc 파일 시스템을 이용한다. 가장 상위 디렉토리는 다음과 같은 위치에 존재하며 다음과 같은 파일을 가진다.


[root@falinux drivers]$ cd /proc/bus/input/[root@falinux input]$ ls -aldr-xr-xr-x    2 root     root            0 Jan  1 00:03 .dr-xr-xr-x    4 root     root            0 Jan  1 00:00 ..-r--r--r--    1 root     root            0 Jan  1 00:03 devices-r--r--r--    1 root     root            0 Jan  1 00:03 handlers/proc/bus/input/devices

devices는 현재 시스템에서 동작하는 디바이스를 나타내는데 예를 들어 키보드, 터치스크린, USB 마우스가 시스템에 연결되어 있다면 devices 파일은 다음과 같은 내용을 포함한다.


[root@falinux input]$ cat devices I : Bus=0003 Vendor=055d Product=0001 Version=0001N : Name=”Samsung Samsung Combo Mini Keyboard”P : Phys=usb-amba-1/input0H : Handlers=kbd B : EV=120013 B : KEY=10000 7 ff800000 7ff febeffdf ffefffff ffffffff fffffffe B : MSC=10 B : LED=1f I : Bus=0003 Vendor=045e Product=0040 Version=0121N : Name=”Microsoft Microsoft Wheel Mouse Optical□P : Phys=usb-amba-2/input0H : Handlers=mouse0 ts0 B : EV=17 B : KEY=70000 0 0 0 0 0 0 0 0 B : REL=103 B : MSC=10 I : Bus=0019 Vendor=1013 Product=9300 Version=0000N : Name=”Cirrus Logic EP93xx Touchscreen”P : Phys=ep93xx_ts/input0H : Handlers=mouse1 ts1 B : EV=b B : KEY=20 0 0 0 0 0 0 0 0 0 0 B : ABS=11000003

이 파일을 보면 현재 3개의 디바이스가 연결되어 있음을 알 수 있다. 각각의 레코드는 다음과 같은 의미들을 갖는다.


◆ I는 디바이스의 정보 중 연결된 버스 번호, 생산자 ID, 제품 ID, 버전을 보여준다.
◆ N는 디바이스 모델명을 알려준다.
◆ P는 내부적으로 표현되는 물리적인 장치 명을 가리키는데 이것은 디바이스 드라이버가 표현하는 값이다.
◆ H는 이 디바이스 드라이버와 연결되어 동작하는 이벤트 핸들러가 어떤 것인지를 표현한다. 응용 프로그램은 이곳에 표시된 이벤트 핸들러 중 하나를 열어서 입력 값을 얻을 수 있다.
◆ 그 외에는 제어와 관련된 값으로 그냥 참고용으로 생각해도 무방하다. 물론 디바이스 드라이버를 디버그하는 단계에서는 이 값은 중요한 의미를 가진다.


/proc/bus/input/handlers


handlers는 동작하고 있는 디바이스와 연결될 수 있는 모든 핸들러의 정보를 표현한다.


[root@falinux input]$ cat handlers N : Number=0 Name=kbd N : Number=1 Name=mousedev Minor=32 N : Number=2 Name=tsdev Minor=128


이 출력 결과는 현재 커널에서 동작되고 있는 핸들러에 kbd, mousedev, tsdev 세 가지 핸들러가 있다는 의미가 되며 Minor는 해당 핸들러 디바이스 파일의 부번호 시작 번호에 대한 정보가 된다.


이벤트 핸들러 디바이스 드라이버


이벤트 핸들러 디바이스 드라이버는 응용 프로그램이 입력 장치들에 입력 값들을 읽기 위한 실제 디바이스 드라이버 파일로 두 가지 기능을 수행한다. 첫 번째는 입력 장치에 의해서 전달된 입력 값들을 내부 버퍼에 적절한 변환을 거쳐서 저장하는 기능을 수행한다. 두 번째는 저장된 입력 값들을 응용 프로그램에서 읽을 수 있도록 파일 입출력 처리를 담당한다. 현재 배포중인 커널 2.6.16 커널에는 다음과 같은 이벤트 핸들러 디바이스 드라이버가 포함되어 있다.


◆ 키보드 디바이스 드라이버(kbd)
◆ 마우스 디바이스 드라이버(mousedev,mice)
◆ 조이스틱 디바이스 드라이버(joydev)
◆ 이벤트 디바이스 드라이버(evdev)
◆ 터치스크린 디바이스 드라이버(tsdev)


이렇게 5가지로 나누어지는 것은 각각의 입력 장치마다 특성이 다르기 때문에 이를 응용 프로그램에서 선택적으로 각각의 특성에 맞게 읽어 들일 수 있도록 처리한 것이다. 이 중 터치스크린과 관련이 있는 이벤트 디바이스 드라이버와 터치스크린 디바이스 드라이버를 제외한 것은 간략하게 소개 정도만 설명하겠다.


키보드 이벤트 핸들러 디바이스 드라이버


키보드 이벤트 핸들러 디바이스 드라이버는 커널 소스 상에 linux/ drivers/char/keyboard.c 에 구현되어 있는데 이 이벤트 핸들러는 다른 이벤트 핸들러와 다르게 직접적으로 응용 프로그램에서 열 수 없다. 내부적으로 가상 콘솔 디바이스 드라이버와 연결되어 있기 때문이다. 스위치 입력을 특정 키보드 입력으로 처리하거나 USB 키보드 장치를 다루고자 한다면 키보드 이벤트 핸들러와 연결되도록 하여야 한다. USB의 경우는 표준 키보드 클래스가 구현된 경우라면 USB 디바이스 드라이버에 의해서 적절히 연결되기 때문에 특별히 고려할 것은 없다. 이 키보드 이벤트 핸들러에 전달되는 키보드 값은 로우 키 값들이다. 이렇게 전달된 값들은 가상 콘솔의 키보드 입력으로 맵핑되는 과정을 거쳐 응용 프로그램에서 사용할 수 있는 적절히 형태로 변환되어 전달된다.


마우스 이벤트 핸들러 디바이스 드라이버


마우스는 사용된 역사만큼이나 다양한 종류가 존재한다. 리눅스 커널은 이 모든 것들을 모두 수용하여 구연하고 있다. 아직까지도 이전 방식으로 마우스의 입력을 처리하는 응용 프로그램이 많기 때문에 마우스 디바이스 드라이버 역시 이전 방식으로 처리될 수 있도록 구현하고 있다. 이런 다양성의 통일화를 위해서 마우스 이벤트 핸들러라는 방식으로 통합되어 가고 있으며 USB 마우스 장치는 반드시 마우스 이벤트 핸들러 디바이스 드라이버와 연결된다.


마우스 이벤트 핸들러는 linux/drivers/input/mousedev.c에 구현되어 있다. 이 핸들러를 사용하는 마우스 디바이스 드라이버들은 보통 linux/drivers/input/mouse 디렉토리 하부에 구현하고 있다. 시스템에 마우스의 장치가 연결되면 응용 프로그램은 다음과 같은 디바이스 파일을 열어서 입력 값을 전달받을 수 있다. 이 디바이스 파일들은 /dev/input/ 하부 디렉토리에 다음과 같은 주 번호와 부 번호를 할당하여 문자형 디바이스 형식으로 mknod와 같은 유틸리티를 사용하여 수동으로 생성시켜 주어야 한다.


crw-r--r--   1 root     root      13,  32 Mar 28 22:45 mouse0crw-r--r--   1 root     root      13,  33 Mar 29 00:41 mouse1crw-r--r--   1 root     root      13,  34 Mar 29 00:41 mouse2crw-r--r--   1 root     root      13,  35 Apr  1 10:50 mouse3......crw-r--r--   1 root     root      13,  62 Apr  1 10:50 mouse30crw-r--r--   1 root     root      13,  63 Apr  1 10:50 mice

주 번호는 13이며 부 번호는 32부터 63까지 할당할 수 있다. 여기서 주의해서 봐야 할 것은 mice 디바이스 파일이다. mouse라는 이름이 붙은 디바이스 파일은 실제 마우스 장치마다 하나씩 대응된다. 예를 들어 USB 키보드와 일반 마우스를 연결했다면 각각은 mouse0 와 mouse1에 해당하게 된다. 하지만 사용자 입장에서 보면 두 개의 마우스는 어떤 것을 사용하였던 간에 동일한 영역의 화면의 위치를 지정하기 때문에 이와 관련된 처리가 필요하다. 그래서 부 번호가 63인 mice 디바이스 파일은 다르게 처리되도록 되어 있다. mice 디바이스 파일은 부 번호가 63이므로 이 디바이스 파일을 연 응용 프로그램은 실제로 연결된 모든 마우스 장치가 발생한 위치 변화 값을 받을 수 있다. 그래서 X 시스템과 같은 응용 프로그램에서 마우스 장치를 지정하려면 /dev/input/mice를 지정해야 한다. 만약 /dev/input/mouse0 만을 지정한다면 USB 마우스와 같이 추가된 마우스 장치는 입력을 받을 수 없게 되기 때문이다.


마우스와 이외의 장치도 이 디바이스 파일을 통하여 값을 읽어 들일 수 있다. 터치스크린이나 디지타이저 역시 이 디바이스 파일을 이용하여 좌표 값을 읽어 들일 수 있다. 왜냐하면 마우스 이벤트 핸들러 디바이스는 내부적으로 크게 3가지 형식으로 값을 전달받기 때문이다. 첫 번째는 전통적인 마우스 이동의 변화 값을 처리하는 방식이다. 이것은 커널 소스 상에서 mousedev_rel_event 함수를 통하여 구현되고 있다. 두 번째는 디지타이저와 같은 형식의 입력인데 좌표의 절대 값을 읽어 들인다. 이것은 커널 소스 상에서 mousedev_abs_event 함수를 통하여 구현된다. 세 번째는 터치패드와 형식의 입력인데 이것은 커널 소스 상에서 mousedev_ touchpad_event 함수로 구현된다. 터치패드는 노트북에 조그맣게 장착된 것을 말한다. 터치스크린과 혼동하지 말기 바란다. 터치패드는 이전 좌표와 연동하여 변화 값을 전달하기 때문에 첫 번째와 같은 동작 방식을 응용 프로그램은 전달 받는다.


여기서 문제가 되는 것은 절대 값을 처리하는 두 번째 방식이다. 이 방식으로 전달된 이벤트 값은 응용 프로그램이 절대 좌표를 처리되도록 구현되어 있어야 한다. X의 경우라면 절대 값을 처리하도록 구현되어 있지만 QT의 시스템은 mice로 열어서 읽을 수 있지만 절대 값 처리가 되어 있지 않기 때문에 처리되지 않는다. 마이크로윈도우 역시 마우스 형식으로 읽어 들이면 문제가 발생한다. 이런 경우는 해당 소스를 직접 패치하여 절대 값을 처리하도록 수정할 필요가 있다.


그러나 터치스크린을 이용하는 시스템일 경우 입력 장치가 마우스 이벤트 핸들러와 연결되어 동작하도록 한다. 즉 응용 프로그램이 터치스크린을 다루기 위해서 마우스 디바이스 파일과 다른 이벤트 핸들러 디바이스 파일을 동시에 열어서 처리하지 않는다는 의미이다. 또한 실질적인 사용상의 이유로 마우스 시스템과 터치스크린 시스템을 동시에 사용하도록 해야 하는 경우는 거의 없기 때문에 필자 역시 입력 장치 처리와 관련된 시스템 구성에서 이와 관련된 작업은 고려하지 않는다.


조이스틱 이벤트 핸들러 디바이스 드라이버


게임과 같은 응용 프로그램은 조이스틱 입력 장치를 다룰 필요가 있다. 조이스틱 입력 장치의 입력 값을 읽어 들이기 위해서는 조이스틱 이벤트 핸들러 디바이스 드라이버를 읽어 들여야 한다. 조이스틱 이벤트 핸들러는 linux/drivers/input/joydev.c에 구현되어 있다. 이 핸들러를 사용하는 마우스 디바이스 드라이버들은 보통 linux/ drivers/input/joystick 디렉토리 하부에 구현하고 있다.


시스템에 조이스틱 장치가 연결되면 응용 프로그램은 다음과 같은 디바이스 파일을 열어서 입력 값을 전달받을 수 있다. 이 디바이스 파일들은 /dev/input/ 하부 디렉토리에 다음과 같은 주 번호와 부 번호를 할당하여 문자형 디바이스 형식으로 mknod와 같은 유틸리티를 사용하여 수동으로 생성시켜 주어야 한다.


crw-r--r--   1 root     root      13,   0 Apr  1 10:50 js0crw-r--r--   1 root     root      13,   1 Apr  1 10:50 js1crw-r--r--   1 root     root      13,   2 Apr  1 10:50 js2crw-r--r--   1 root     root      13,   3 Apr  1 10:50 js3...crw-r--r--   1 root     root      13,   31 Apr  1 10:50 js31

이벤트 이벤트 핸들러 디바이스 드라이버


이벤트 이벤트 핸들러 디바이스 드라이버는 장치 독립적인 범용적인 형식으로 입력 값을 받아들이기 위한 디바이스 드라이버이다. 이것은 입력 장치마다 하나씩 생성된다. 이 디바이스 파일을 모든 장치 즉 키보드, 마우스, 조이스틱, 터치스크린과 같은 장치의 값을 읽어 들일 수 있다. 아마도 장래에는 이 형식으로 리눅스 입력 장치들은 통합될 것이다. 독자 여러분이 응용 프로그램이 만약 터치스크린을 다루도록 할 예정이라며 이 이벤트 디바이스 파일을 열어서 처리하도록 설계하는 것이 미래 지향적일 것이다. 그러나 앞에서도 언급했듯이 이 핸들러 디바이스 드라이버는 진행형이다. 그렇다고 크기 바뀔 만한 내용은 필자가 보기에 발견되지 않았다.


이벤트 이벤트 핸들러는 linux/drivers/input/evdev.c에 구현되어 있다. 시스템에 입력장치가 연결되면 응용 프로그램은 다음과 같은 디바이스 파일을 열어서 입력 값을 전달받을 수 있다. 이 디바이스 파일들은 /dev/input/ 하부 디렉토리에 다음과 같은 주 번호와 부 번호를 할당하여 문자형 디바이스 형식으로 mknod와 같은 유틸리티를 사용하여 수동으로 생성시켜 주어야 한다.


crw-r--r--   1 root     root      13,  64 Apr  1 10:49 event0crw-r--r--   1 root     root      13,  65 Apr  1 10:50 event1crw-r--r--   1 root     root      13,  66 Apr  1 10:50 event2crw-r--r--   1 root     root      13,  67 Apr  1 10:50 event3...crw-r--r--   1 root     root      13,  95 Apr  1 10:50 event31

응용 프로그램은 이 디바이스 파일을 읽어 들이기 위해서는 read 함수를 이용하여 읽을 수 있는데 이때 읽어 들이는 버퍼의 형식은 다음과 같아야 한다.


struct input_event {   struct timeval time;   unsigned short type;   unsigned short code;   unsigned int value;};time 

이 필드는 이벤트가 발생한 시간을 의미한다.


type, code, value


이 필드는 code와 value가 나타내는 값의 형식을 의미한다. type과 code는 리눅스 커널 소스의 include/linux/input.h 에 정의되어 있는데 터치스크린과 관련된 대표적인 값은 다음과 같다.


#define EV_SYN   0x00  // 동기를 맞추어야 한다. #define EV_KEY   0x01  // 키 또는 버튼이다. #define EV_ABS   0x03  // 절대 좌표 값이다.* type == EV_KEY

타입 값이 EV_KEY라면 터치스크린에서 버튼의 입력이 발생했음을 알리게 된다. 이때 code는 대부분의 경우 BTN_TOUCH로 표현되는 상수 값을 갖는다. value는 버튼의 상태를 표현한다. 다음 중 한 가지 상태가 된다.


0 : 터치가 떨어졌다. 즉 사용자가 터치스크린에서 입력을 위한 접촉을 떨어뜨렸다.
1 : 터치되었다. 즉 사용자가 위치를 지정하기 위해서 접촉했다.
2 : 터치가 연속되어 발생하고 있다. 그러나 이 값은 실제로 터치에서 잘 사용하지 않는다.


* type == EV_ABS


타입 값이 EV_ABS라면 터치스크린의 접촉된 절대 좌표 값이 변경되었음을 알리게 된다. 이때 code는 ABS_X 나 ABS_Y가 된다. value는 이때의 좌표 위치가 된다.


ABS_X : X 좌표 절대 값이다.
ABS_Y : Y 좌표 절대 값이다.

* type == EV_SYN


타입 값이 EV_SYN는 이벤트가 최종적으로 발생되었다는 것을 알리는 값이다. code와 value는 0이 된다. 이 타입이 존재하는 이유는 이벤트 전달은 한 번에 하나씩 밖에 전달하지 못하기 때문이다. 사용자가 터치스크린을 터치하면 실제로 발생하는 이벤트는 총 3가지이거나 1가지이다.


1. [키가 눌렸다. X 좌표 값은 342다. Y 좌표 값은 128이다.]
2. [키가 떨어졌다.]


이와 같은 두 가지 상태가 있다면 이 두 가지 경우를 실제 앞 구조체의 필드에 대입하면 다음과 같은 형태가 된다.


  [터치가 눌린 상태]type = EV_KEY, code = BTN_TOUCH, value = 1,   <--  키가 눌렸다.type = EV_ABS, code = ABS_X    , value = 342, <--  X 좌표 값은 342다.type = EV_ABS, code = ABS_Y    , value = 128, <--  Y 좌표 값은 128이다.type = EV_SYN, code = 0        , value = 0,   <--  지금까지 데이터가 하나의 데이터다.     [터치가 떨어진 상태]type = EV_KEY, code = BTN_TOUCH, value = 0,   <--  키가 떨어졌다.type = EV_SYN, code = 0,         value = 0,   <--  지금까지 데이터가 하나의 데이터다.

이벤트 이벤트 핸들러는 거꾸로 응용 프로그램에서 이벤트를 집어넣을 수 있다. 방법은 읽기와 반대로 이벤트 디바이스 파일에 write 함수를 이용하여 struct input_event 구조체의 필드를 적절히 채워서 넣으면 된다.


터치스크린 이벤트 핸들러 디바이스 드라이버


이름 상으로 보면 이 이벤트 디바이스 드라이버가 터치스크린과 가장 알맞은 처리 구조를 가지고 있다. 실제로 이벤트 핸들러 디바이스 드라이버는 iPAQ H3600이라는 PDA의 터치스크린을 위해 설계되고 작성된 디바이스 드라이버이다. 터치스크린에 필요한 칼리브레이션과 관련된 파라미터를 설정할 수 있도록 ioctl이 설계되어 있기 때문에 이 디바이스 드라이버를 사용해보는 것도 좋은 방법일 수 있다. 아니면 자신의 시스템에 맞게 이 이벤트 핸들러 디바이스 드라이버를 수정하거나 또는 재작성하여 이벤트 핸들러를 등록하는 것도 좋을 것이다. 하지만 이 디바이스 드라이버는 아직까지 범용적으로 처리될 수 있는 구조로 되어 있지 않으며 응용 프로그램이 사용하는 라이브러리에 연동되어 사용되기에 몇 가지 문제점을 가지고 있다. 많은 부분이 iPAQ H3600에 맞추어져 있다. 그래서 이 연재에서는 이 터치스크린 이벤트 핸들러 대신에 이벤트 이벤트 핸들러 디바이스 드라이버를 이용하는 것을 중심으로 설명한다.


터치스크린 이벤트 핸들러는 linux/drivers/input/tsdev.c에 구현되어 있다. 시스템에 입력 장치가 연결되면 응용 프로그램은 다음과 같은 디바이스 파일을 열어서 입력 값을 전달받을 수 있다. 터치스크린 디바이스 드라이버들은 보통 linux/drivers/input/touch screen 디렉토리 하부에 구현하고 있다. 이곳에 만들어진 터치스크린 디바이스 드라이버들 중에 터치스크린 이벤트 핸들러를 사용한 것은 h3600_ts_input.c이다.


이 디바이스 파일들은 /dev/input/ 하부 디렉토리에 다음과 같은 주 번호와 부 번호를 할당하여 문자형 디바이스 형식으로 mknod와 같은 유틸리티를 사용하여 수동으로 생성시켜 주어야 한다.


crw-r--r--   1 root     root      13,  128 Apr  1 10:49 ts0crw-r--r--   1 root     root      13,  129 Apr  1 10:50 ts1crw-r--r--   1 root     root      13,  130 Apr  1 10:50 ts2crw-r--r--   1 root     root      13,  131 Apr  1 10:50 ts3...crw-r--r--   1 root     root      13,  159 Apr  1 10:50 ts31

입력 디바이스 드라이버 구현


터치스크린에서 발생하는 사용자 입력을 응용 프로그램에 전달하기 위해서는 터치스크린의 제어를 담당하고 입력된 값을 처리하는 디바이스 드라이버가 필요하다. 예를 들어 EP9312나 S3C2410과 같은 프로세서는 내부적으로 터치스크린 컨트롤러를 가지고 있는데 터치 컨트롤러의 상태를 읽고 난 후 읽어 들인 눌림 상태나 좌표 값의 AD 변환 값을 적절히 절대 좌표 값과 버튼 값으로 변환하여 응용 프로그램에서 읽도록 해야 한다. 이런 과정은 다음과 같은 입력 디바이스 드라이버로 동작하기 위한 기본 구현 과정이 구현되어야 한다. 우선 터치스크린이 해야 할 동작을 살펴보자.


◆ 입력 디바이스 드라이버로의 등록과 이벤트 핸들러와의 연결
◆ 장치 제거시 입력 디바이스 드라이버에서 제거와 이벤트 핸들러와의 연결 종료
◆ 입력된 이벤트 상태의 보고


입력 디바이스 드라이버로의 등록과 이벤트 핸들러와의 연결


입력 디바이스 드라이버로 등록되고 이벤트 핸들러와 연결되기 위해서는 장치 특성을 기술하는 내용과 연결되어야 할 이벤트 핸들러의 특성을 기술하는 내용을 담은 struct input_dev 구조체 변수를 작성하고, 이것을 input_register_device() 함수를 이용하여 등록해야 한다. input_dev 구조체는 많은 필드를 가지고 있지만 터치스크린을 제어하고자 하는 경우에 필요한 필드들은 다음과 같다.


등록 정보 표현을 위해 필요한 필드들

입력 장치가 INPUT 디바이스 드라이버에 등록되면 /proc/bus/ input/devices라는 파일에 자세한 등록 정보가 표현되어 응용 프로그램에서 참고할 수 있다. 커널은 이 파일에 표현되는 정보를 터치스크린 디바이스 드라이버가 등록하는 input_dev 구조체 변수의 name,phys,id 필드의 값을 이용해서 표시한다.


◆ name : 이 필드에는 장치명을 구체적으로 기술한다


. .name = “Cirrus Logic EP93xx Touchscreen”,


◆ phys : 이 필드에는 장치의 연결 상태와 실제 장치명을 지정하는데 큰 의미는 없다.


.phys = “ep93xx_ts/input0”,


◆ id : 장치의 제품 정보와 관련된 필드를 지정한다. 이 필드는 다음과 같은 구조체 형식을 갖는다.


struct input_id {   __u16 bustype;  // 버스 타입   __u16 vendor;   // 제작사 구분 ID   __u16 product;  // 제품 구분 ID   __u16 version;  // 버전};

설정 예는 다음과 같다.


.id = {   .bustype = BUS_HOST,   .vendor  = PCI_VENDOR_ID_CIRRUS,   .product = 0x9300,   .version = 0,   },

이벤트 핸들러의 연결을 지정하는 필드들

터치스크린은 다음과 같은 이벤트 핸들러와 연결되어야 할 필요가 있다.


◆ 마우스 이벤트 디바이스 드라이버(mousedev,mice)
◆ 이벤트 이벤트 디바이스 드라이버(evdev)
◆ 터치스크린 이벤트 디바이스 드라이버(tsdev)


등록되는 터치스크린 디바이스 드라이버가 앞의 이벤트 핸들러와 연결되기 위해서는 다음과 같은 필드들의 설정 상태를 정확하게 지정해야 한다.


unsigned long evbit[NBITS(EV_MAX)]; // 이벤트 타입
unsigned long keybit[NBITS(KEY_MAX)]; // 사용되는 키 타입
unsigned long absbit[NBITS(ABS_MAX)]; // 사용되는 절대 좌표 값


이 필드 변수 값들이 어떻게 설정되어야 하는지 알고 싶다면 각 이벤트 핸들러 소스 상에서 struct input_device_id 구조체로 선언된 변수의 값들을 살펴보면 된다. 디바이스 드라이버가 등록될 때 이 필드와 struct input_device_id 구조체에 선언된 것과 비교하여 일치할 경우만 해당 핸들러들이 연결되기 때문이다. 터치스크린이라면 보통 다음과 같이 설정하면 mousedev와 tsdev 이벤트 핸들러에 연결된다. 이벤트 이벤트 핸들러에는 등록 조건과 상관없이 무조건 설정되기 때문에 터치 디바이스 드라이버는 신경 쓸 필요가 없다.


.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },


◆ evbit는 필드는 터치스크린이 처리해야 할 데이터 타입을 정의한다. EV_KEY는 버튼의 입력을 처리한다는 의미이고 EV_ABS는 절대 좌표 값을 처리한다는 의미이다.


◆ keybit는 사용되는 키 값이 어떤 것이 있는지를 표현한다.


◆ absbit는 이벤트 좌표 값이 형식이 된다. ABS_X와 ABS_Y는 각각 절대 좌표 형식의 X와 Y를 처리한다는 것을 표현한다.


여기서 사용되는 각 필드는 각 기능을 표현하는 1비트짜리 데이터를 포함할 수 있는 배열이다. BIT 매크로는 수치에 해당하는 비트를 표현하며 LONG 매크로는 비트 번호에 해당하는 배열 인덱스 값을 반환한다.


응용 프로그램이 장치를 열거나 닫을 때 처리해야 할 내용을 지정하는 함수 필드

응용 프로그램이 터치스크린의 입력 값을 얻기 위해서 마우스 이벤트 핸들러 디바이스 파일이나 터치 이벤트 핸들러 디바이스 파일 또는 이벤트 이벤트 핸들러 디바이스 파일을 열었을 때 터치 디바이스 드라이버 파일은 특정한 일을 수행할 필요가 있을 경우가 대부분이다. 보통 터치스크린 컨트롤러들은 터치 상태의 변화를 인터럽트를 이용하여 전달하는데 이 인터럽트는 응용 프로그램이 터치스크린을 사용하지 않는 경우라면 굳이 동작할 필요가 없다. 그래서 보통은 디바이스 파일을 응용 프로그램이 열었을 때 인터럽트 설정을 하며 닫을 때 인터럽트 설정을 해제한다. 이렇게 디바이스 파일이 열렸다는 것을 통보받거나 닫혔다는 것을 통보받기 위해서 사용되는 input_dev 구조체 필드는 open과 clode이다. 다음은 해당 사용 예를 나타낸다.


.open = ep93xx_ts_open,
.close = ep93xx_ts_close,


터치스크린 디바이스 드라이버 등록 예

이렇게 설정된 구조체 변수는 INPUT 디바이스 드라이버의 input_register_device() 함수를 사용하여 등록한다. 다음과 같은 형태가 터치스크린 디바이스를 등록하는 한 예이다.


static int ep93xx_ts_open(struct input_dev *dev){}static void ep93xx_ts_close(struct input_dev *dev){}static struct input_dev ep93xx_ts_dev = {   .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },   .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },   .absbit = { BIT(ABS_X) | BIT(ABS_Y) },   .open = ep93xx_ts_open,   .close = ep93xx_ts_close,   .name = “Cirrus Logic EP93xx Touchscreen”,   .phys = “ep93xx_ts/input0”,   .id = {      .bustype = BUS_HOST,      .vendor  = PCI_VENDOR_ID_CIRRUS,      .product = 0x9300,      .version = 0,   },};static int __init ep93xx_ts_init(void){   input_set_abs_params(&ep93xx_ts_dev, ABS_X, TS_X_MIN, TS_X_MAX, 0, 0);   input_set_abs_params(&ep93xx_ts_dev, ABS_Y, TS_Y_MIN, TS_Y_MAX, 0, 0);   input_register_device(&ep93xx_ts_dev);   :   :}

INPUT 디바이스 드라이버에 터치스크린을 등록하는 위치는 보통 모듈이 초기화되는 함수에서 처리한다. 앞의 예를 보면 input_set_abs_params() 함수가 사용되는데 이것은 이벤트로 전달되는 X, Y 값을 변화하여 처리하기 위한 것이다. 터치스크린의 입력 값들은 보통 AD 변환 값이므로 이 값은 터치된 위치와 정확하게 일치하지 않는다. 그래서 이 값들은 내부적으로 적절히 변환되어 처리되어야 하고 일정한 범위를 벗어나지 않아야 한다. 이런 각 좌표 값의 최소 값과 최대 값을 input_set_abs_params() 함수를 이용하여 지정하는 것이다.


이제 장치 제거시 입력 디바이스 드라이버에서 제거와 이벤트 핸들러와의 연결 종료 외부에서 전달된 이벤트의 처리를 살펴보자. 터치스크린이라면 장치가 제거될 경우는 거의 없다. 그러나 USB 마우스 같은 경우에는 해당 장치가 제거될 가능성을 배제할 수 없다. 이런 경우에는 해당 디바이스 드라이버의 모듈이 제거될 수도 있으므로 이때는 INPUT 디바이스 드라이버의 관리 구조체와 연결된 이벤트 핸들러와의 연결을 끊을 필요가 있다. 이때 사용하는 함수는 input_unregister_device() 함수이다. 보통은 다음 예와 같은 형태로 모듈 제거 함수에서 처리된다.


static void __exit ep93xx_ts_exit(void){   input_unregister_device(&ep93xx_ts_dev);}

이 함수가 호출될 경우에는 input_dev 구조체의 close에 선언된 함수가 반드시 호출된다.


입력된 이벤트 상태의 보고


터치스크린에 어떤 상태 변화가 생겼다면 터치스크린 디바이스 드라이버는 이벤트 핸들러 디바이스 드라이버로 전달하여 응용 프로그램에서 해당 이벤트를 받을 수 있도록 전달해야 한다. 이때 이벤트를 전달하기 위한 가장 기초적인 함수는 input_event 이다. 이 함수는 다음과 같은 형태로 되어 있으며 사용하기 편하게 하기 위해 여러 매크로 형태로 제공하고 있다.


원형
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);

매크로
void input_report_key(struct input_dev *dev, unsigned int code, int value);
void input_report_rel(struct input_dev *dev, unsigned int code, int value);
void input_report_abs(struct input_dev *dev, unsigned int code, int value);
void input_sync(struct input_dev *dev);


dev는 터치스크린 디바이스 드라이버가 등록한 input_dev 구조체 변수이다. type, code, value는 앞에서 설명한 이벤트 이벤트 핸들러 디바이스 드라이버에서 설명했던 input_event 구조체의 필드와 같은 내용이다. 터치스크린에서 이벤트가 발생하는 경우는 다음과 같은 세 가지 경우로 볼 수 있다.


1. 터치가 눌렸다.
2. 터치가 눌린 상태로 이동한다.
3. 터치가 떨어졌다.


이 중 1과 2는 같은 상태로 보는 것이 좋다. 이 두 가지를 코드로 구현하면 다음과 같은 형식이 된다. 매크로를 사용했을 경우를 예로 들겠다.


터치가 눌렸다 / 터치가 눌린 상태로 이동한다
input_report_key(&ep93xx_ts_dev , BTN_TOUCH, 1);
input_report_abs(&ep93xx_ts_dev , ABS_X,342 );
input_report_abs(&ep93xx_ts_dev , ABS_Y,128 );
input_sync(&ep93xx_ts_dev);

터치가 떨어졌다
input_report_key(&ep93xx_ts_dev , BTN_TOUCH, 0);
input_sync(&ep93xx_ts_dev);


이렇게 전달된 이벤트는 연결된 이벤트 핸들러 디바이스 드라이버 모두에게 전달된다. 각 이벤트 핸들러 디바이스 드라이버는 각각의 형식에 맞게 이벤트를 변경하고 버퍼에 저장한다. 이 값은 응용 프로그램에서 형식에 맞게 읽어 들이게 된다. 그래서 어떤 이벤트 핸들러 디바이스 드라이버를 응용 프로그램이 열더라도 문제가 없게 되는 것이다.


INPUT 디바이스 드라이버 관련 함수 중 터치와 관련된 함수 정리

헤더 파일 : linux/include/linux/input.h

static inline void init_input_dev(struct input_dev *dev);


dev에 전달된 구조체를 초기화한다.


void input_register_device(struct input_dev *);


입력 장치를 등록한다.


void input_unregister_device(struct input_dev *);


입력 장치를 제거한다.


void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);


이벤트를 이벤트 핸들러 디바이스 드라이버에 전달한다.


static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);


내부적으로 input_event 함수를 이용하여 버튼 또는 키 이벤트를 전달한다.


static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);


내부적으로 input_event 함수를 이용하여 이동된 크기 이벤트를 전달한다.


static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);


내부적으로 input_event 함수를 이용하여 이동 된 절대 좌표 이벤트를 전달한다.


static inline void input_sync(struct input_dev *dev);


내부적으로 input_event 함수를 이용하여 하나의 상태에 대한 여러 이벤트가 동기되어야 할 필요가 있다는 것을 전달한다.


static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat);


절대 좌표 값을 지정하는 경우 값의 최소 값과 최대 값을 지정한다.


커널 컴파일 옵션 설정


터치 디바이스 드라이버를 작성했다면 이를 포함시키고 동작할 수 있도록 커널 컴파일 옵션을 적당히 설정해야 한다. 다음은 EZ-EP9312 보드에서 사용되는 커널의 경우에 설정된 예제 화면이다. 설정해야 하는 항목은 다음과 같다.



<화면 1> 커널 컴파일 옵션 설정


1. (1024) Horizontal screen resolution
2. (768) Vertical screen resolution
3. <*> Touchscreen interface
4. (1024) Horizontal screen resolution
5. (768) Vertical screen resolution
6. <*> Event interface
7. <*> EP93xx Keyboard support
8. [*] EP93xx USB Keyboard support
9. [*] Mice
10. [*] Touchscreens
11. <*> EP93xx touchscreen


1, 2, 3, 4는 이벤트 핸들러가 좌표 값을 변환하기 위해서 사용하는 터치스크린의 해상도 값이다.


아쉬움을 뒤로하고


뭐 항상 그렇듯이 필자의 무계획성의 성격 때문에 처음 계획했던 글과 실제로 써지는 글은 항상 차이가 있다. 처음에는 입력 장치 디바이스 드라이버 구조 이외에도 해당 디바이스 드라이버를 사용하는 응용 애플리케이션에 대한 부분까지 언급하려 했으나 지면 관계상(?) 이 부분의 설명은 곤란하게 되었다. 아마도 한 번의 컬럼으로써 다루기에 너무 많은 내용이 있는 것이 아닐까 생각된다. 다음에 나머지 부분을 설명하는 것을 약속하며 마쳐야겠다.