디바이스 드라이버를 커널에 빌트인으로 포함시키기

kenGwon·2024년 2월 29일
0

[Embedded Linux] BSP

목록 보기
34/36

우리가 만든 디바이스 드라이버를 커널에 포함시켜서 배포해보자.

우리가 앞전 수업에서 만든 p432_ledkey_poll을 커널에 포함시켜보겠다.

디바이스 드라이버를 자동으로 적재하는 방법을 적용해보겠다.

내가 사용하지도 않는 디바이스 드라이버가 항상 적재가 되어있는것도 곤란하다.
부팅하면서 컴퓨터에 연결되어 있는 하드웨어 장치들이 뭔지를 체크하고 그것에 대한 디바이스 드라이버만 적재하는 것이 일반적이다.

PC같은 경우에는 이더넷 카드를 빼고 낀다던지 하는 경우가 있을 수 있지만, 임베디드 시스템의 경우 왠만해서 설계단계에서 들어간 하드웨어가 바뀔일이 없다. 그래서 디바이스 드라이버가 바뀔 일도 거의 없다. 그래서 빌트인으로 커널에 집어넣는 경우가 종종있다.

  1. 빌트인으로 커널에 집어넣는 방법
  2. 모듈로 만들어서 디바이스 드라이버를 넣고 뺄수 있도록 하는 방법

빌트인으로 집어넣는 법

메뉴 콘피그로 정보 얻기

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

메뉴 콘피그로 가서 [Device Drivers] -> [Character Devices] 경로로 진입하면 아래의 화면이 뜬다. 아래 사진에 있는 것들은 모두 캐릭터 디바이스이다. 이 디바이스에 대한 설정 파일들이 커널 소스의 어떤 부분에 있는지를 확인하고 싶은 것이다.

그냥 가장 먼저 나오는 브로드컴 디바이스가 어딨는지 알기 위해 <Help>를 들어가 본다.

그러면 defined at에 설정되어 있는 경로에 설정파일이 들어가 있다는 것을 확인할 수 있다. 그러면 우리가 넣고 싶은 캐릭터디바이스 설정파일도 위와 비슷한 경로에 집어넣어야겠다는 정보를 얻을 수 있게 된다.

우리는 위의 정보를 바탕으로 서브디렉토리를 만들고 파일을 수정하는 등의 작업을 할 것이다.

디바이스 드라이버 어디에 넣는지 알아보기

우선 디바이스 드라이버를 어디에 집어넣을지를 고민해봐야한다.

ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/char$ pwd
/home/ubuntu/pi_bsp/kernel/linux/drivers/char

디바이스 드라이버는 위 경로에 넣는 것이 일반적이다.
여기에 1. 소스파일을 넣고 2. Kconfig를 설정해주고 3. Makefile을 수정해줘야 한다.
그래야 커널의 build system을 통해서 디바이스 드라이버를 빌트인으로 넣을 수 있다.

0. 캐릭터 디바이스 통합 설정파일에 새로 추가하려는 옵션 넣기

ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ vi drivers/char/Kconfig


위의 코드 구조를 보면 위에서 menuconfig 명령을 통해서 확인했던 text 기반 GUI의 동작 기반이 된 구조를 확인할 수 있다. (브로드 컴)


8번 라인에 새로 명령어를 추가한다. 서브디렉토리를 새로 만들어서 독립적으로 Kconfig를 만듬으로써 나중에 유지보수를 편리하도록 하겠다.

이제 새로운 디렉토리를 만들었으니 해당 디렉토리를 Makefile이 참조할 수 있도록 Makefile도 수정해줘야 한다.

ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ vi drivers/char/Makefile


6번 라인에 새로 명령어를 추가했다. 만약 CONFIG_KCCI_LEDKEY가 y로 셋팅되면 Makefile은 빌드할 때 kcci_ledkey/ 폴더 안에 있는 Makefile을 참조하게 된다.(해당 디렉토리의 Makefile만 보는 것이다.(당연한 것))

obj-y는 빌드할때 무조건 넣겠다는 것이다. 그리고 obj-$(변수) 방식은 빌드할 때 y가 될수도 있고, m(모듈)이 될수도 있고, 주석이 될수도 있다.

1. 폴더 만들고 소스 파일 가져오기

// 폴더 생성 및 이동
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ mkdir drivers/char/kcci_ledkey
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ cd drivers/char/kcci_ledkey/

// 디바이스 드라이버 소스파일 가져오기
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/char/kcci_ledkey$ cp /home/ubuntu/pi_bsp/drivers/p432_ledkey_poll/*dev.c .
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/char/kcci_ledkey$ ls
ledkey_dev.c

기존 코드는 insmod / rmmod 명령어를 통해서 적재 / 해제 되는 코드였다.
그런데 이걸 커널이 부팅할 때 자동으로 적재가 되고, 커널이 shut down될때 자동으로 제거되는 방식으로 만들고 싶다.
이렇게 하려면 디바이스 드라이버 소스파일의 코드를 몇라인 수정해줘야 한다.

ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/char/kcci_ledkey$ vi ledkey_dev.c

__init이 붙은 함수들은 커널이 부팅될 때 자동으로 실행된다.
__exit이 붙은 함수들은 커널이 종료될 때 자동으로 실행된다.

2. Kconfig 만들기

ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/char/kcci_ledkey$ vi Kconfig


(Kconfig는 메뉴를 만들어주는 것이다.)

우리가 만든 GPIO API 코드는 ARM 아키텍처에 의존적인 코드이다. 그래서 아키텍처가 ARM으로 선택되어있어야 menuconfig에 메뉴를 띄우겠다는 것이 depends on ARM의 의미이다.
config KCCI_LEDKEY 라고 선언하면 자동으로 CONFIG_KCCI_LEDKEY로 변환되어 옵션에 들어가게 된다.(이게 아래의 Makefile에서 옵션 여부를 체크할 때 사용하는 변수로 사용되게 되는 것이다.)

tristate부터 적히는 것들은 서브 메뉴 구성이다. 우리는 현재 메인메뉴(menu) 하나에 서브메뉴(tristate) 하나만 넣는 것이다.
help라는 키워드를 넣어야지 menuconfig에서 자동으로 help 정보를 구성해준다.

3. Makefile 만들기

ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/char/kcci_ledkey$ vi Makefile

다시 메뉴 콘피그 켜서 확인해보기

아까 위에서 들어갔던 메뉴 경로(device drivers -> character devices)에 가보면 새로운 메뉴가 생겼다!


여기서 옵션을 M(모듈)로 주느냐 *(빌트인)으로 주느냐 공백(포함 안함)으로 주느냐에 따라서 설정이 달라지는 것이다.


우리는 빌트인으로 할거기 때문에 별표 해주고 저장하면서 나간다.

이렇게 메뉴를 바꿔주고 나와서 아래 경로에서 .config파일을 열어보면 우리가 설정한 내용이 반영되어 있는 것을 확인할 수 있다.

ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ vi .config


위의 작업을 통해 3241번째 라인이 새로 추가된 것이다.
최상위 .config에서 확인한 CONFIG_KCCI_LEDKEYy로 되어있으므로, 위에서 만든 Makefile의 변수자리에 y가 들어가면서 이제 빌드할 때 컴파일할 때 포함하도록 된 것이다.

이제 설정이 완료되었으니 컴파일 해본다.

ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j4
  SYNC    include/config/auto.conf.cmd
  CALL    scripts/checksyscalls.sh
  CC      drivers/char/kcci_ledkey/ledkey_dev.o
  AR      drivers/char/kcci_ledkey/built-in.a
  AR      drivers/char/built-in.a
  AR      drivers/built-in.a
  AR      built-in.a
  ...
  (이하 생략)

이렇게 다시 컴파일을 해보면 방금 우리가 설정한대로 drivers/char/kcci_ledkey/ledkey_dev.o가 포함되어 컴파일 된 것을 확인할 수 있다.


커널 업그레이드 하기

수정사항이 반영된 커널이미지 zImage를 라즈베리파이로 복사해가서 커널을 업그레이드 해보겠다.

// 우분투 가상환경에서 NFS로 라즈베리파이로 커널 이미지 넘김
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ cp arch/arm/boot/zImage /srv/nfs

// 라즈베리파이에서 커널 이미지 받아서 overwrite
pi@pi14:/mnt/ubuntu_nfs $ sudo cp zImage /boot/firmware/kernel7l.img

부팅 확인

이렇게 부팅을 해보면 정상적으로 부팅되는 것을 확인할 수 있다.

pi@pi14:~ $ uname -a
Linux pi14 6.1.77-v7l+ #21 SMP Thu Feb 29 10:04:18 KST 2024 armv7l GNU/Linux

방금 전에 빌드한 이미지가 적용된 것을 확인할 수 있다.

디바이스 드라이버 빌트인 적재 확인

pi@pi14:~ $ sudo cat /proc/devices
...
230 ledkey_dev
...

부팅하자마자 insmod를 친 적이 없는데, 부팅하자마자 디바이스 드라이버가 적재되어 있는 것을 확인할 수 있다.

그럼 이제 이 디바이스 드라이버가 잘 작동하는지 어플리케이션을 통해서 확인해보자.
그전에, 우리는 디바이스 드라이버만 적재했지 아직 디바이스 파일을 만든 상태는 아니기 때문에 먼저 디바이스 파일을 만들고 어플리케이션을 실행해야 한다.

pi@pi14:/mnt/ubuntu_nfs $ sudo mknod /dev/ledkey_dev c 230 0
pi@pi14:/mnt/ubuntu_nfs $ sudo chmod 666 /dev/ledkey_dev

이제 실행해보자.

pi@pi14:/mnt/ubuntu_nfs $ ./ledkey_app 0x0f


잘 실행되는 것을 확인할 수 있다.

여기서 한가지 짚어 넘어가야할 것이 있다.
insmod로 추가한 명령만 lsmod에 출력된다. 우리는 지금 빌트인으로 모듈을 적재했기 때문에 lsmod 목록에 디바이스 드라이버가 출력되지 않는다.

profile
스펀지맨

0개의 댓글