3 분 소요

지난 글에서 직접 쓴 디바이스 트리로 보드를 부팅시켰다.

root@myir:~# cat /proc/device-tree/model
KNV SC6_0 (MYC-YA157C-V2)

여기까지는 좋았다. 문제는 그다음부터 뭘 봐야 하는지였다.

부팅이 됐다고 페리페럴이 다 사는 건 아니다. 디바이스 트리에 열 몇 개를 적었으니 그게 각각 커널에 어떻게 나타나는지 확인해야 하는데, 숫자를 세다 보니 기대값을 모르면 판단이 안 된다는 걸 알게 됐다.


기대값을 먼저 적는다

DTS에서 활성화한 것들을 목록으로 뽑았다.

DTS에 쓴 것 리눅스에서 어떻게 보일까
uart4, usart3, uart5, uart7, uart8 /dev/ttySTM* 5개?
i2c4, i2c6 (i2c1/i2c2는 끔) i2cdetect -l 2개
timers1/2/4/8 /sys/class/pwm/ 4개
gpio-leds 6개 /sys/class/leds/ 6개
adc1 채널 0,1 /sys/bus/iio/devices/
sdmmc3 + brcmfmac wlan0

이 표를 먼저 만든 게 결과적으로 잘한 일이었다. 틀린 것과 원래 그런 것을 구분할 기준이 생겼기 때문이다.


사례 1 — 5개를 기대했는데 4개

root@myir:~# ls /dev/ttySTM*
/dev/ttySTM0  /dev/ttySTM1  /dev/ttySTM2  /dev/ttySTM3

DTS에는 시리얼을 다섯 개 열었다. 콘솔(uart4), U3(usart3), U5(uart5), U7(uart7), U8(uart8).

하나가 비었다. 어느 게 안 뜬 건지 찾다가, DTS를 다시 보고 알았다.

&uart7 {
        /* ... */
        bluetooth {
                compatible = "brcm,bcm43438-bt";
        };
};

uart7에는 자식 노드로 블루투스 칩이 달려 있다. 이런 경우 커널은 그 포트를 serdev(serial device bus)로 잡아서 블루투스 드라이버에 직접 연결한다. 일반 tty로는 노출되지 않는다.

4개가 정상이었다. 5개가 나왔다면 오히려 블루투스가 안 붙은 것이다.

포트 개수를 셀 때는 aliases에 적은 수가 아니라, 자식 노드가 달린 포트를 뺀 수를 기대해야 한다.


사례 2 — 껐으니 안 나오는 게 맞다

root@myir:~# i2cdetect -l
i2c-0   i2c   STM32F7 I2C(0x5c002000)   I2C adapter
i2c-1   i2c   STM32F7 I2C(0x5c009000)   I2C adapter

두 개만 나왔다. 주소로 확인하면 0x5c002000이 I2C4, 0x5c009000이 I2C6이다. 둘 다 우리가 쓰는 버스다.

벤더 BSP는 I2C1과 I2C2도 켜뒀지만, 우리 보드에서 그 핀들은 다른 용도였다.

  • I2C2(PH4/PH5) → 우리는 AC 검출 입력
  • I2C1(PB6/PD13) → LCD 전용이었는데 패널을 안 씀

그래서 DTS에서 명시적으로 껐고, 안 나오는 게 의도한 결과다.

그런데 만약 기대값 표를 안 만들었다면 “I2C가 두 개밖에 없네, 뭐가 잘못됐나” 하고 한참 뒤졌을 것이다. 끈 것도 적어둬야 한다.


사례 3 — 에러가 났는데 동작한다

WiFi 로그에 빨간 줄이 있었다.

brcmfmac mmc0:0001:1: Direct firmware load for
  brcm/brcmfmac43430-sdio.knv,stm32mp157a-sc6.txt failed with error -2

-2ENOENT, 파일이 없다는 뜻이다. 그런데 바로 다음 줄을 읽으니 얘기가 달랐다.

brcmfmac: brcmf_c_preinit_dcmds: Firmware: BCM43430/1 wl0:
  Sep 11 2018 09:22:09 version 7.45.98.65 (r707797 CY) FWID 01-b54727f

펌웨어가 정상 로드됐다. ip link를 보니 인터페이스도 있었다.

4: wlan0: <BROADCAST,MULTICAST> mtu 1500 ...
    link/ether 40:fd:f3:ec:c5:78

brcmfmac은 보드별 설정 파일(<compatible>.txt)을 먼저 찾고, 없으면 일반 파일로 fallback한다. 실제로 디렉터리를 보니 이랬다.

root@myir:~# ls /lib/firmware/brcm/ | grep 43430
brcmfmac43430-sdio.AP6212.txt
brcmfmac43430-sdio.myir,stm32mp157c-ya157c-v2.txt
brcmfmac43430-sdio.txt                              ← 이걸로 대체됨
brcmfmac43430-sdio.bin
brcmfmac43430-sdio.clm_blob
BCM43430A1.hcd

스캔도 잘 됐다.

root@myir:~# iw dev wlan0 scan | grep -E "SSID|signal"
        signal: -67.00 dBm
        SSID: KNVISION
        signal: -76.00 dBm
        SSID: WINIX SMART Air
        ...

-67 dBm이면 연결에 충분하다. SDIO 배선, 전원 제어(WL_REG_ON), 펌웨어, 안테나까지 전부 정상이라는 뜻이다.

그럼 저 에러는 왜 났나

내가 compatible을 바꿨기 때문이다.

compatible = "knv,stm32mp157a-sc6", "myir,stm32mp157c-ya157c-v2", "st,stm32mp157";

brcmfmac은 보드의 첫 번째 compatible 문자열로 설정 파일 이름을 만든다. 문자열을 바꿨으니 찾는 파일 이름도 바뀌었고, 그런 파일은 당연히 없다.

링크 하나면 정리된다.

cd /lib/firmware/brcm
ln -sf "brcmfmac43430-sdio.myir,stm32mp157c-ya157c-v2.txt" \
       "brcmfmac43430-sdio.knv,stm32mp157a-sc6.txt"

동작에는 지장이 없지만, 보드 전용 캘리브레이션 값을 쓰는 게 맞으니 링크를 걸어두는 편이 낫다.

compatible을 바꾸면 커널 안에서만 끝나지 않는다. 펌웨어 파일명, udev 규칙, 유저 공간 설정이 그 문자열을 참조하고 있을 수 있다.


사례 4 — 에러가 없는데 안 된다

앞과 정반대 경우도 만났다. WiFi에 연결하려고 connmanctl을 쳤다.

root@myir:~# connmanctl
Error getting technologies: The name net.connman was not provided by any .service files

connmanctl은 있는데 안 된다. 이건 D-Bus 에러로, net.connman 서비스가 등록되어 있지 않다는 뜻이다. 클라이언트만 있고 데몬이 안 돌고 있었다.

root@myir:~# systemctl status connman
   Loaded: loaded (/lib/systemd/system/connman.service; disabled; ...)
   Active: inactive (dead)

disabled. 이미지에 설치는 되어 있는데 자동 시작이 꺼져 있었다.

systemctl start connman
systemctl enable connman     # 다음 부팅에도 유지

“설치되어 있다”와 “돌고 있다”는 다르다. 명령어가 존재한다고 서비스가 준비된 건 아니다.


기대대로였던 것들

나머지는 표대로 나왔다.

root@myir:~# ls /sys/class/pwm/
pwmchip0  pwmchip4  pwmchip8  pwmchip12

TIM1/2/4/8 네 개. 각각 B_AC_ON, PWM_CP, C_AC_ON, PWM_WDI용이다. 부모 타이머를 status = "okay"로 켜는 걸 빠뜨렸다면 하나도 안 나왔을 것이다.

root@myir:~# ls /sys/class/leds/
bled  dout1  dout2  dout3  dout4  rled

여섯 개. 상태 LED 둘과 디지털 출력 넷이다. 이제 유저 공간에서 바로 토글할 수 있다.

echo 1 > /sys/class/leds/dout1/brightness

DOUT1PG10 → ULN2803 달링턴 드라이버 → 12V 부하로 나간다. 회로에서 유도한 극성(GPIO_ACTIVE_HIGH)이 맞는지 단자에서 실측할 차례다.


정리 — 검증은 세는 일이 아니다

이번에 네 가지 경우를 만났다.

  보인 것 실제
ttySTM 5개 중 4개 정상 (BT가 serdev로 빠짐)
I2C 4개 중 2개 정상 (2개는 의도적으로 끔)
WiFi 에러 로그 동작함 (fallback 성공)
connman 에러 없음 안 돎 (데몬 미실행)

개수만 세거나 로그만 봐서는 넷 다 틀리게 판단한다. 각각 “왜 그 값이어야 하는지”를 알아야 맞고 틀림이 갈린다.

그래서 검증 전에 기대값 표를 만든 게 유용했다. 표에 적을 때 이미 한 번 “이건 왜 이 개수지?”를 생각하게 되고, 실제 결과와 어긋나면 어긋난 이유를 찾을 수 있는 상태가 된다.

DTS를 쓸 때 주석에 근거를 남겨두라고 했던 것과 같은 맥락이다. 검증도 기대값을 먼저 글로 적어두면 훨씬 빨라진다.

다음은 PLC 모뎀 드라이버를 모듈로 올리고, DOUT/DIN 극성을 실측으로 확인할 차례다.

댓글남기기