본문 바로가기

PROJECT/ex347_project

LDD_Part 1. 디바이스 드라이버의 이해

※ Device Driver ?


커널과 주변 장치간에 데이터를 전달 하는 프로그램이다.
Device Driver 는 정의 되어 있는 인터페이스(Interface)로 커널과 연결된다.


※ Linux Device Driver 종류


L.D.D
에는 

Character Device Driver
Block Device Driver
Network Device Driver

가 있다.

 
 

어떻게 장치와 커널이 연결 되는가?


LINUX
에서는 모든것을 파일로 관리한다
Board
에 연결된 장치도 그 장치를 표현하는 하나의 "장치 파일(device file)" 로 관리 된다.
그리고 그 장치 파일을 사용할 수 있도록 해주는 것이 장치드라이버(Device Driver)이다.

(디바이스 드라이버는 다바이스를 file로 보고 관리한다.)

Device Driver는 정해진 Function 으로 장치를 제어하도록 작성된다.
응용프로그램은 정해진 Function으로 디바이스 드라이버의 Function을 사용함으로써 장치를 사용할 수 있게 된다.




※ 기초 
디바이스 드라이버 


디바이스
드라이버를 작성하려는데 감이 오질 않을 것이다.
혹은 너무 오랜만에 작성하기에 기억이 가물가물할 것이다.
어떻게 작성하는지 들어가기 전에 우선 작성 단계를 짚어보자.



ф 디바이스 드라이버 작성 단계
 

.디바이스 드라이버 초기화 인터페이스 구현

(주번호 할당 디바이스 드라이버 루틴 등록)

ex)) register_chrdev(240, "gpio_dev", &chr_fops)
 

.디바이스 드라이버 커널 인터페이스 구현

(entry point functions, file_operations)

ex))  

struct file_operations chr_fops =  {

    .owner      = THIS_MODULE,

    .ioctl      = mini_gpio_ioctl,

    .release    = mini_gpio_release,  }; 

 // mini_gpio_ioctl, mini_gpio_release 와 같은 함수는 구현해 줘야 한다.
 

.디바이스 드라이버 하드웨어 인터페이스 구현

(레지스터 접근)

ex)) S3C2410_GPF(0)  와 같이 H/W 레지스터에 접근해야 할경우 그 주소값을 정의(define)해줘야 한다.

       <mach/regs-gpio.h>와 같이 다른 헤더파일에 정의해서 include 해서 사용하는 것이 보편적임.
 

.장치 파일 생성

ex))  # mknod /dev/mydrv [b|c] major_number minor_number

 

.응용 프로그램 작성


.커널 컴파일 리부팅

 

위의 디바이스 드라이버 작성단계를 보면 디바이스 드라이버를 만드는데 3계의 단계( 위에서부터 순서대로 3 ) 거친다.
과정을 좀더 자세히 보자. 

 

ф 디바이스 드라이버의 3부분


      
.초기화 인터페이스

init()함수 ( init_module()함수 )
      register_chrdev()
      register_blkdev()
      register_irq()
                 ...... 

 

     .시스템 인터페이스(커널 인터페이스)

문자/블록 드리이버의 경우 file_operations구조체 이용

struct file_operations{

open, release, read, write, ioctl, ...

}

네트워크 드라이버의 경우

open, close, transmit, receive, ioctl 같은 함수를 사용한다.

( 다른 시스템 함수를 사용함 )

 

     .하드웨어 인터페이스 

  통상(Memory Mapped I/O) 레지스터 주소값을 사용한다.

===> I/O Mapped I/O (혹은 Port Mapped I/O ) 경우 입출력을 위해

 특정 포트(Port) 사용한다.     in(), out()

 

위에서도 얘기했듯이 리눅스에서는 디바이스를 파일로 관리된다.

그리고 파일을 장치파일 (Device File)이라고 부른다.
 

ф 장치 파일(Device File)


      
.리눅스에서는 모든 하드웨어를 파일로 추상화 한다.

(/dev/ 디렉토리의 파일들은 실제 하드웨어를 표현한다.)

 

주번호(Major Number) : 응용 프로그램과 디바이스 드라이버를 연결하는 고리 역할 

부번호(Minor Number) : 실질적인 하드웨어


    
.Device File 만드는 (inode 지원하는 filesystem에서만 가능)

mknod <장치파일명> <종류> <주번호> <부번호>

ex> monok /dev/gpio c 240 22

 

디바이스 드라이버도 사용에 따라서 구분을 해놓는다.
(
날이갈 수록 문자 드라이버와 블록 드라이버 차이가 점점 없어져 가긴하지만...)
구분은 다음과같다..

ф 디바이스 드라이버 종류

     .문자 디바이스 드라이버 (character device driver)

임의의 길이를 갖는 문자열을 다루는 디바이스 드라이버.

 

     .블록 디바이스 드라이버 (block  device driver)

일정 크기의 버퍼(커널 내부의 버퍼) 통해 데이터를 처리하는 디바이스 드라이버.

 

     .네트워크 디바이스 드라이버 (network  device driver)

네트워크층과 연결된 디바이스 드라이버로 응용프로그램에서 직접적인 처리가 불가능하다.
커널 내부에 있는 네트워크 프로토콜 스택과 연동해서 사용한다

 

디바이스 드라이버를 만든 다음에 이를 어떻게 사용할 것인가?
우리는 응용프로그램에서 장치를 사용하기 위해서 디바이스 드라이버를 만들었다.
그럼 어떻게 자원의 처리 요청해야 할까?
방법으로는 다음과 같이 2가지 방법 생각할 있을 것이다



ф 자원처리 요청 방법 

     1.
시스템 호출(system call)

시스템 콜을 사용할 경우 어떻게 작동할지 알아보자.
            o.
기능 별로 system call번호를 부여
            o.
번호에 해당하는 제어 루틴을 커널 내부에 정의
              o.
응용프로그램에서 원하는 기능 번호를 레지스터에 저장 원하는 기능을

호출(system call)
              o.system call
발생 하면 제어권은 커널이 같는다.
              o.
커널내의 서비스 루틴 기능 번호를 살펴보고, 맞는 서비스 루틴을 호출
              o.
서비스 루틴이 모두 초리된 제어권을 넘겨받음

 

이와 같은 순서대로 시스템 호출은 이루어 진다.
이때 기능에 따라서 system call 번호를 부여하는데...
일일이 디바이스 드라이버에 system call 번호를 부여한다면 system call 번호가 너무 많아질 것이다.

그럼 관리해야 하는 system call 증가하고 필요 없는 기능일지라도 커널소스에 포함되어 있어서 커널의 크기가 커지는 단점이 생긴다.


 

 

     2.파일 입출력 형식을 이용한 디바이스 드라이버 사용

 

<<과정>>
     o. 
일반 파일을 제어하듯이 디바이스 파일에 입출력을 시도하고,

o. 커널내에 해당 디바이스 파일에 연결된 디바이스 드라이버의 서비스 루틴이 호출되서 처리를 한다.(제어권은 커널로...)
o. 
서비스 루틴에서의 모든 처리가 완료되면 제어권은 다시 응용프로그램으로 넘겨진다.

 

이와 같은 방식을 사용하면 시스템 호출을 이용해서 구현 때의 문제가 해결된다(system call 번호문제, 커널의 크기문제)

이렇게 하기 위해 모듈이라는 형식을 빌려서 만든다!!

 

모듈


ф
 
모듈의 구현 원리


커널
라이브러리를 객체(.ko)형태로 만들어 시스템콜을 통해 커널에 적재 요청하고,
요청에 따라 커널은 해당 객체를 커널에 동적으로 링크하여 사용한다.
이를 구현하기 우해 커널에 심볼 테이블 기능이 추가 됐다.


/proc/ksyms v2.4 kernel 소스에서 커널 심볼 테이블이고,
v2.6 kernel에서는
/proc/kallsyms 바뀌었다.


 

ф 모듈 유틸리티


    
.# insmod <모듈명>.ko

커널에 모듈 추가

 

     .# rmmod <모듈명>

커널에서 모듈 제거

 

     .# lsmod

현재 등록된 모듈을 확인
/proc/modules 파일을 읽어서 모듈 정보를 얻는다

 

     .# modprobe 

모듈의 의존성 검사 추가, 삭제(-r)

 

     .# depmod 

모듈의 의존성 검사 의존성 업데이트

                 

ф 모듈 프로그램의 기본 형태
 

 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 MODULE_LICENSE("GPL");

 int my_module_start() {
     ...
    return 0;
 }
 
 void my_module_end() {
     ... 
 }
 
 module_init(my_module_start);
 module_exit(my_module_end); 

 


새로운 디바이스 드라이버 모델


디바이스 드라이버는 애플리케이션 혹은 커널이 장치에 접근 가능한 인터페이스를 만들어준다.

그리고 애플리케이션은 /dev 디렉토리에 있는 노드로 디바이스에 명령을 내리고, /sys 디렉토리에 있는 노드로 디바이스 정보를 수집한다.

 

디바이스 모델

           디바이스 드라이버에서 공통점을 추출해서 버스와 코어 계층으로 밀어넣음.

((기존 디바이스 드라이버에서는 chrdev를 사용했다. chrdev를 사용할 경우에는 별도의 구조체를 선언하지 않고 register_chrdev함수에 file_operation 구조체를 포함시키면서 chrdev를 등록하기만할 뿐이었다.))

          

디바이스 모델에 대해서 배우기 위해 알아야 할 구성요소들에 대해서 알아보자.

다음에 배우는 udev, sysfs, kobject, 디바이스 클래스 /dev 노드관리, 핫플러그, 펌웨어 다운드, 모듈 자동 적재 같은 핵심 커널 하위 시스템에 영향을 미친다.

 

아래에 구성요소에 대한 설명을 했다.



ф구성요소

    ㅡ.udev


          
초기에는 모든 노드를 /dev디렉토리 아래에 정적으로 만들어 주었다.

           kernel 2.4버전부터 devfs를 도입하여 동적으로 디바이스 노드를 생성함.

           그런데 작명은 여전히 디바이스 드라이버에 의존해서 생성시킴

 

           udev디바이스 관리를 사용자 영역으로 올리기 위해서 등장함.

udev는 데몬(daemon)과 같이 동작해서 kernel이 uevent를 발생시는 것을 감시한다.
(uevent는 장치를 접속시키거나 제거될때 발생한다.)

udev 는 작업을 수행하는 데 있어 다음 내용에 의존한다.

1.     커널 sysfs 지원

sysfs는 메모리 내부 파일 시스템으로 시동 시동 시점에서 /sys 아래에 마운트 된다.

(설정 방법은 /etc/fstab을 살펴보자)

2.     udevd, udevinfo 같은 사용자 영역 데몬과 유틸리티 집합

3.     /etc/udev/rules.d/ 디렉터리에 있는 사용자 지정 규칙

디바이스를 일관성있게 바라보기 위해서는 규칙을 만들어야 하기도 함.

 

udev sysfs내부의 해당 파일에서 제품 속성을 가져온다.


 

    ㅡ.sysfs


          
/proc
처럼 커널의 자료구조를 볼 수 있도록 해준다.

           (sysfs는 메모리 내부 파일시스템으로 시동 시점에서 /sys아래에 마운트 된다.)


  

    ㅡ.kobject


          
공통 객체 속성의 추상화를 지원한다.

          


 <kobject의 주요 필드>

1.     참조 카운트 관리를 수행하기 위한 kref 객체

 

2.     kset에 대한 포인터

kset kobject가 속한 객체 집합이다.


3.     kobj_type

kobject를 기술하는 객체 유형이다.


 

    ㅡ.디바이스 클래스


          
드라이버를 사용하기를 원하는 인터페이스이다.

           클래스 인터페이스는 디바이스마다 더 큰 디바이스 클래스에 속한다는 생각을 추상화 함.

( 아래 그림의 경우를 보면 misc라는 디바이스 클래스에 rtc라는 디바이스가 속하게 됨을 볼 수 있다. )


<Tying the pieces of the device model>

위의 그림은 RTC 드라이버 모듈에 대해서 일종의 블럭도를 그린 것이다.
'modprobe rtc'라는 명령이 사용자영역으로부터 입력되면 rtc 모듈을 추가 시킨다.
그러면 초기화 함수인 'rtc_init()'이 실행된다.
실제 리눅스 커널소스의 'driver/rtc/' 디렉토리 안에 들어가 보면 많은 rct코드들을 접할 수 있을 것이다.

rtc
드라이버 모듈은 misc 디바이스 이기 때문에 'misc_register (&rtc_dev)' 명령으로 모듈이 추가 된다.
'misc_register()'의 호출은 그림에 보이는 바와 같이 맨왼쪽의 “/sys/class/misc/rtc/”를 생성하고 그 디렉토리 안에 “uevent”“dev”를 생성시킨다. 마지막으로 “/dev/rtc”를 생성해낸다.

맨처음 만들어지는 "/sys/class/misc/rtc/" 디렉토리는 misc 장치인 rtc의 디렉토리를 만들어 준 것이다.
그리고 이 디렉토리에 추가되는 'dev 파일'에는 디바이스에 할당된 주 번호와 부번호를 포함한다.
'uevent 파일'은 콜드플러깅을 위해 사용된다.  (아래 핫플러그와 콜드 플러그의 3번째 문단에 설명돼있음)

'/dev/rtc' 는 애플리케이션이 rtc드라이버에 접근시 사용하는 장치파일이다.


 

ф핫플러그와 콜드 플러그~!


핫플러그란 동작중인 시스템에 장치가 연결되는 것, 콜드플러그란 장치가 연결된 뒤에 시스템에 시동을 가한것.(미리 장치 붙인후 전원 ON)


커널은 핫플러그를 감지하면 netlink 소켓을 거쳐서 사용자 영역으로 uevent를 보낸다.
그러면 디바이스의 생성/삭제를 관리하는 udevd uevent를 받아서 핫플러그를 관리한다.


udev
로 콜드 플로그도 처리 가능~!
시동 시점에서 커널이 모듬 디바이스에 대해서 sysfs 아래 uevent 파일을 생성하고 이 파일에 콜드 플러그를 보낸다.
udev가 시작할 때 /sys디렉토리 아래의 모든 uevent 파일을 읽어서 콜드 플러그로 연결된 디바이스마다 핫플러그 uevent를 발생 시킨다.


 

'PROJECT > ex347_project' 카테고리의 다른 글

5/11  (0) 2011.05.11
spi 보던 사이트  (0) 2011.05.11
크로스컴파일(Cross Compile) 환경 구축  (1) 2011.05.09
Mini2440] GPIO-LED-module 만들기  (0) 2011.04.25
분석!!!]]]Mini2440_gpio-LED] 의 하나를 gpio 로 빼보자.  (0) 2011.04.25