Device Driver
PC와 외부 하드웨어 장치가 서로 신호를 주고 받을 수 있도록 통신하는 것을 도와주는 소프트웨어
Firmware에서 임베디드 개발
- 중간 Layer가 없다 --> H/W memory map address에 직접 값 access가 가능
- 단점 : H/W 장비 교체 시, 모든 Firmware의 H/W 관련 코드를 수정해야 한다.
Kernel
- 공통적으로 쓰는 API 제공 --> 중간 layer 역할
- Kernel 소스코드만 새로운 H/W가 동작되도록 수정해서 다시 build하면, 다른 firmware를 수정할 필요 없음
- 단점 : 빌드 시간이 매우 길다
Kernel Module ( != Device Driver)
- 커널에 들어가는 코드 덩어리
- 디바이스 드라이버를 "커널 모듈 형식"으로 제작
- 커널 모듈만 동작 중인 커널에 적재 / 해제하는 방식으로 테스트하는 개념을 착안
- 디바이스 드라이버를 빌드하면 ,결과물이 커널 모듈 형식으로 만들어지고, 커널 안에 적재/해제할 수 있다.
Device Driver의 장점
- App / Driver 역할 분담
Application 개발자 | Device Driver 개발자 |
HW를 제어할 수 있는 API 사용법 익히기 API가 건네 준 Data 처리 API를 이용하여 HW 제어 Log 남기기 Network / UI 작업 ... |
HW 제어 API 제작 HW 불량 분석 Latency 측정 ... |
타 OS 에서 Device Driver 개발
• Windows : Windows Driver Kit (WDK) 으로 개발
- C++
- WDM ( Windows Driver Model ) : MS에서 Driver 개발 표준화 시킨 모델
- 개발자 센터 : https://learn.microsoft.com/ko kr/windows hardware/drivers/
• MacOS : I/O Kit Framework 으로 개발
- C++
- Application 개발 / Kernel 용 Driver 개발 파트로 나눠 개발
- I/O Kit Document : https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/IOKitFunda mentals/Introduction/Introduction.html
메모리맵에 GPIO 핀을 매핑하고, GPIO 핀에 H/W 장치를 연결하여 해당 메모리 주소에 신호를 줘서 동작시키는 방법
-> Memory Mapped I/O
Device Driver 제작
커널 소스코드
- 운영체제의 소스코드
- C로 작성, GPL 라이선스 사용
커널 소스코드를 수정한다는 의미
-> 수정된 소스코드를 컴파일해서 나만의 새로운 커널 개발
다양한 시스템에 적용할 수 있고, 사용자의 요구에 따라 맞춤형 운영체제 커널 제작이 가능
운영체제 커널의 동작방식을 이해, 시스템 최적화, 버그 수정 등
운영체제 개발자, 시스템 엔지니어들은 커널 소스코드에 대한 이해가 필수
1. 커널 소스코드 만들기 - "~/test/hi.c" 파일 생성
1 // 리눅스 커널 모듈 헤더 파일을 포함
2 #include <linux/module.h>
3
4 // 모듈 라이선스를 GPL로 설정 > 소스 코드를 공개, 수정 및 배포 권한을 사용자에게 부여
5 MODULE_LICENSE("GPL");
6
7
8 static int hi_init(void)
9 {
10 // 로드될 때 "OK HELLO KFC" 메시지를 출력
11 printk( KERN_INFO "OK HELLO KFC\n");
12 // 성공적으로 로드되었음을 나타내는 0을 반환
13 return 0;
14 }
15
16
17 static void hi_exit(void)
18 {
19 // 언로드될 때 "BYE BYE" 메시지를 출력
20 printk( KERN_INFO "BYE BYE\n\n");
21 }
22
23 // hi_init 함수를 모듈 초기화 함수로 지정
24 module_init(hi_init);
25 // hi_exit 함수를 모듈 종료 함수로 지정
26 module_exit(hi_exit);
특징
- main 함수가 없음
- 라이센스 설정이 들어감
- printk 사용
- 커널 내부 모듈끼리 함수 중복을 피하기 위해 함수명을 "모듈이름_역할" 형태로 사용
- 모든 함수에 static 적용
2. Makefile 생성
1 # 현재 실행 중인 커널 버전에 대한 헤더 파일 경로를 변수로 설정
2 KERNEL_HEADERS=/lib/modules/$(shell uname -r)/build
3
4 # obj-m은 빌드할 커널 모듈의 이름을 hi.o로 지정
5 obj-m := hi.o
6
7 # 'go' 라벨은 커널 모듈을 빌드하는 명령을 실행
8 go:
9 # 현재 디렉토리의 소스 코드를 사용하여 커널 모듈을 빌드
10 make -C $(KERNEL_HEADERS) M=$(PWD) modules
11
12 # 'clean' 라벨은 빌드 프로세스에서 생성된 파일을 삭제하는 명령을 실행
13 clean:
14 # 커널 모듈 빌드에 사용된 파일들을 삭제
15 make -C $(KERNEL_HEADERS) M=$(PWD) clean
- C : 해당 디렉토리로 이동해서 make를 수행 --> 해당 디렉토리에 있는 Makefile로 make를 실행
- M=$(PWD) : 결과물이 현재 디렉토리에 생성 --> 해당 디렉토리의 Makefile의 결과물을 현재 디렉토리에 생성
3. Makefile 실행
4. Kernel 내부 동작 상태 파악
# 최근 커널로그 출력
$ sudo dmesg
# 커널로그 모니터링
$ sudo dmesg -w
모니터링을 켜놓으면, 적재 / 제거 결과를 실시간으로 확인할 수 있다.
5. 커널 모듈을 파일에 적재 / 제거
Device Driver를 build 후 생성 된 파일(.ko)을 Kernel에 적재 / 제거할 수 있다.
# insmod
# Device Driver를 제작해서 빌드한 파일(.ko)를 Kernel에 적재 | Kernel이 해당 module을 관리하기 시작
$ sudo insmod hi.ko
# rmmod
# 필요없는 Device Driver Module을 제거
$ sudo rmmod hi
6. 결론
- Application 개발 시, Device File에다가 System Call API 만 쓰면, 장치 제어가 된다.
- 만약 H/W가 바뀌면 , Kernel을 다시 Build 할 필요가 없다. Device Driver 만 다시 작성하면 된다.
Kernel Module vs Device Driver
Kernel Module
- Kernel에 집어 넣을 수 있는 덩어리
- insmod / rmmod를 사용해 실행 가능
Device Driver
- HW 장치를 제어할 수 있는 프로그램
- Driver 개발 완료 후, 배포 시 Kernel에 포함시켜 Kernel 전체 Build 후 배포
--> Linux에서는 Device Driver를 Kernel Module 형태로 개발할 수 있다.
현재 시스템에 설치된 커널에 대한 디렉토리를 살펴보면, /usr/src로 심볼릭 링크가 되어있다.
이유는, 커널 소스 코드와 커널 헤더 파일의 경로를 일일이 수정하지 않기 위함이다.
예를 들어, 시스템에 두 가지 버전의 커널이 설치되어 있다고 가정해보자.
- 5.4.0-26-generic
- 5.8.0-50-generic
이 경우, 커널 소스 코드와 헤더 파일은 다음과 같은 경로에 위치할 것이다.
- /usr/src/linux-headers-5.4.0-26-generic
- /usr/src/linux-headers-5.8.0-50-generic
커널 모듈을 개발하거나 외부 드라이버를 빌드할 때, 커널 헤더 파일에 대한 경로를 지정해야 한다.
만약 커널 버전이 변경되면, 헤더 파일 경로도 다음과 같이 수동으로 수정해야 한다.
- KERNEL_HEADERS_5_4 := /usr/src/linux-headers-5.4.0-26-generic
- KERNEL_HEADERS_5_8 := /usr/src/linux-headers-5.8.0-50-generic
이렇게 되면 커널 버전이 바뀔 때마다 경로를 수동으로 수정해야 하는 번거로움이 생기기 때문에,
리눅스는 각 커널 버전별로 '/lib/modules/<커널 버전>/build' 심볼릭 링크를 생성한다.
이렇게 되면, 현재 실행 중인 커널 버전에 대한 헤더 파일 경로를 다음과 같이 지정할 수 있다.
KERNEL_HEADERS := /lib/modules/$(shell uname -r)/build
모듈 관련 명령어
# Module에 대한 정보 확인
$ modinfo [module]
# 적재된 kernel Module 확인
$ lsmod
Linux 장치 제어 방법
Linux에서 H/W 장치를 제어하려면, 해당 장치에 관한 Device File이 있어야 한다
Device File을 Device Node라고 하며, app 개발자는 syscall을 이용해 장치를 제어할 수 있다.
- open, read, write, close
Device Driver 종류
1. chrdev : 캐릭터 디바이스 드라이버
- Byte 단위로 값 전달
- 일반적인 임베디드 장치에 사용
2. blkdev : 블록 디바이스 드라이버
- Kb 이상의 '블록' 단위로 값 전달
- Disk 장치에 사용되는 디바이스 드라이버
3. netdev : 네트워크 디바이스 드라이버
- Socket을 열고 ioctl이라는 System call로 장치를 제어
/dev 디렉토리
linux에서 Device File을 위한 디렉토리
실제 장치 or 가상 장치일 수 있다
$ ls -al /dev
왼쪽 ㅁ - c, b : Device File을 의미
오른쪽 ㅁ - major / minor number를 의미
Major Number (주번호)
- 디바이스 종류를 나타냄
- 같은 기능을 하는 디바이스가 여러 개 있다면, 같은 주번호를 가짐
Minor Number (부번호)
- 같은 종류 device 에서 구분 용도
- 개발자 마음대로 의미 부여 가능
- blkdev 에서는 파티션 번호로 사용
- node 이름에서 숫자가 붙은 경우 부번호를 의미
netdev는 노드를 사용하지 않음
Device File 생성
# mknod : 노드 파일을 만드는 유틸리티
# 일반적으로 /dev/에 생성해서 관리
# 사용법
$ sudo mknod [filename] [filetype] [majorN] [minorN]
# 예시
$ sudo mknod /dev/nobrand c 100 0
# --> nobrand라는 이름의 캐릭터 디바이스 주번호 100, 부번호 0 생성
# 권한 설정
$ sudo chmod 666 /dev/nobrand
ioctl
input output control을 줄임 / 하드웨어를 제어하기 위한 함수
<sys/ioctl.h> 필요
int ioctl(int fd, unsigned long request, ...);
# ex) ioctl(fd, _IO(0, 3), 0);
- fd : open() 함수로부터 얻은 file descriptor
- request : 사용자 정의 제어 코드
- . . . : 추가 인수가 사용되지 않으므로, 0이 전달; 일반적으로 필요한 추가 데이터를 포인터 타입으로 전달하는데 쓰임
Request 추가 설명
request : cmd 인자를 의미
_IO(type, nr) : 간단한 명령 코드 생성
_IOR(type, nr, data_type) : 읽기 동작이 있는 명령 코드 생성
_IOW(type, nr, data_type) : 쓰기 동작이 있는 명령 코드 생성
_IOWR(type, nr, data_type) : 읽기 및 쓰기 동작이 있는 명령 코드 생성
type: 매직 번호로, 고유한 값을 사용하여 드라이버 간에 구별되는 고유한 값
nr: 명령 번호로, 드라이버 내에서 각 명령을 구별하는 숫자
data_type: 데이터 타입으로, 추가 인수로 전달되는 데이터의 타입, 커널이 사용자 공간과 커널 공간 사이에서 데이터를 안전하게 전송하는 데 필요한 크기를 계산하는 데 사용
cmd 구성
- 약속된 cmd 변수의 비트 단위 Format (32 bit)
Direction : R / W
Size : 데이터 크기
Type : 매직넘버
Number : 구분 번호
사용 방법
1. app.c에 ioctl 정의
2. 위와 같이 device driver의 fops에 함수 정의
3. Driver insmod 후 app 실행
리눅스 임베디드 개발자의 이해
리눅스 임베디드 개발자 : 리눅스를 사용하는 업체에서 필요로 한다
리눅스가 필요한 회사?
-> AP SoC 업체 (삼성 S Lsi)
AP ? : Application Processor ( 앱이 동작하는 고성능 CPU )
SoC ? : System on Chip ( 내부에 다양한 기능이 있는 칩 )
대표적 사례
https://www.samsung-dsrecruit.com/recruits/division_intro/detail/slsi.php
SoC 업체에 취업을 한다 -> 임베디드 리눅스 개발을 한다
-> SoC (칩) 안에 많은 전자 부품이 들어간다
-> 부품들이 원활하게 동작할 수 있도록 디바이스 드라이버를 개발하면 된다.
SoC : 자체적으로 하드웨어도 만들고, 하드웨어가 동작하는 소프트웨어도 만드는 업체
-> 할 일 : 리눅스 코드, 드라이버 코드, 샘플 코드를 제작하게 된다.
--
SoC 업체에서 개발한 칩을 갖고 개발을 시작하는 업체?
-> BSP 업체 : Board Support Package 개발 회사 (임베디드 계의 SI 업체)
SI 업체 : 시스템 통합 업무를 담당하는 업체
SoC 부품회사들의 부품을 가져와 보드에 장착해서 제대로 동작할 수 있는 시스템을 구축하는 업무를 한다.
-> BSP 업체는 SoC가 포함된 보드를 제작하는 업체
-> 야근이 많다, 중소/중견기업이 많다 정도