우리가 만든 디바이스 드라이버를 커널에 포함시켜서 배포해보자.
우리가 앞전 수업에서 만든 p432_ledkey_poll을 커널에 포함시켜보겠다.
디바이스 드라이버를 자동으로 적재하는 방법을 적용해보겠다.
내가 사용하지도 않는 디바이스 드라이버가 항상 적재가 되어있는것도 곤란하다.
부팅하면서 컴퓨터에 연결되어 있는 하드웨어 장치들이 뭔지를 체크하고 그것에 대한 디바이스 드라이버만 적재하는 것이 일반적이다.
PC같은 경우에는 이더넷 카드를 빼고 낀다던지 하는 경우가 있을 수 있지만, 임베디드 시스템의 경우 왠만해서 설계단계에서 들어간 하드웨어가 바뀔일이 없다. 그래서 디바이스 드라이버가 바뀔 일도 거의 없다. 그래서 빌트인으로 커널에 집어넣는 경우가 종종있다.
$ 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을 통해서 디바이스 드라이버를 빌트인으로 넣을 수 있다.
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(모듈)이 될수도 있고, 주석이 될수도 있다.
// 폴더 생성 및 이동
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
이 붙은 함수들은 커널이 종료될 때 자동으로 실행된다.
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 정보를 구성해준다.
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_LEDKEY
가 y
로 되어있으므로, 위에서 만든 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
목록에 디바이스 드라이버가 출력되지 않는다.