충전기 보드 시스템 설정 정리 — 호스트 이름, 네트워크, MAC, 블루투스 복구 (Yocto 레이어 2일차)
오전에는 보드에 손으로 넣은 펌웨어와 도구를 Yocto 레시피로 옮겼다. 오후에는 원래 내일(11/05) 하려던 시스템 설정을 앞당겨 했다. 시작하기 전에 정해야 할 것이 네 가지 있었다.
| 결정할 것 | 정한 것 | 이유 |
|---|---|---|
| 호스트 이름 | 고정 sc6 |
개발 단계에서는 간단한 게 낫다 |
| MAC 주소 | 개발용 고정값 | 회사 MAC 대역을 OTP에 쓰는 건 되돌릴 수 없어 제품 단계로 |
| WiFi 접속 정보 | 이미지에는 빈 템플릿만 | 이미지가 밖으로 나가도 사내 WiFi 키가 따라가지 않게 |
| 블루투스 용도 | 어제처럼 SPP 유지 | 제품 용도는 EVerest 단계에서 정한다 |
결과부터 정리하면 이렇다.
| 항목 | 레이어 | 보드 확인 |
|---|---|---|
호스트 이름 sc6, 시간대 Asia/Seoul |
base-files, tzdata bbappend |
3일차 새 카드에서 |
| PLC는 DHCP 끄기, WiFi DHCP, 경로 우선순위 | sc6-network-config |
✅ |
| MAC 세 개 고정 | 커널 DTS, qca7005-fw |
✅ |
| 블루투스 완전 실패 시 복구 | sc6-bt-check |
✅ |
1. 호스트 이름 — 왜 그렇게 길었나
1-1. 증상
새 OS로 옮긴 뒤 보드의 호스트 이름은 이랬다.
stm32mp15-sc696-0d-a9-64-71-181e-83-f3-f2-17-9a
블루투스로 휴대폰과 페어링할 때도 이 이름이 그대로 보였다.
1-2. 원인: 부팅마다 MAC을 덧붙이는 서비스
ST 이미지에는 st-hostname이라는 서비스가 있다. 부팅할 때마다 end0의 MAC을 호스트 이름 뒤에 붙인다.
MAC=$(ip link show end0 | grep "link/ether" | awk '{print $2;}' | ... | tr ':' '-')
PREVIOUS_HOST=$(cat /etc/hostname)
...
if $(cat /etc/hostname | grep -qe "$MAC"); then
: # 이미 붙어 있으면 그대로
else
echo ${PREVIOUS_HOST}${MAC} > /etc/hostname
기본 이름은 머신 이름 stm32mp15-sc6이다. 우리 보드는 OTP에 MAC이 없어서 그동안 MAC이 한 번 바뀌었다(96:0d:... → 1e:83:...). 그러자 서비스가 “지금 MAC이 이름에 없다”고 판단해 새 MAC을 또 붙였다. 그래서 MAC 두 개가 이어 붙은 이름이 됐다.
1-3. 조치
# recipes-core/base-files/base-files_%.bbappend
hostname = "sc6"
이것만으로는 부족하다. st-hostname이 남아 있으면 sc602-4b-4e-...처럼 또 붙는다. 이 서비스는 st-image-core 이미지 레시피에 들어 있으니, 3일차에 충전기용 이미지를 만들 때 뺀다.
1-4. 시간대와 레시피 이름
# recipes-extended/tzdata/tzdata.bbappend
DEFAULT_TIMEZONE = "Asia/Seoul"
처음에는 습관대로 tzdata_%.bbappend로 만들었다가 No recipes available for ... tzdata_%.bbappend 오류가 났다. oe-core의 시간대 레시피는 버전이 없는 tzdata.bb라서, % 와일드카드가 맞을 버전이 없다. bbappend 이름은 레시피 파일 이름을 그대로 따라야 한다. 고치고 나니 tzdata-core 패키지에 /etc/localtime → zoneinfo/Asia/Seoul이 들어갔다.
2. 네트워크 — PLC 모뎀이 DHCP를 하고 있었다
2-1. 원인 찾기
보드에는 네트워크 인터페이스가 넷이다.
| 인터페이스 | 장치 | 원하는 동작 |
|---|---|---|
end0 |
유선 이더넷 (YT8511) | DHCP |
eth0 |
QCA7005 PLC 모뎀 (SPI) | DHCP 없음, IPv6 링크 로컬만 |
wlan0 |
AP6212 WiFi | DHCP, 유선보다 낮은 우선순위 |
usb0 |
USB OTG 가젯 | ST 기본 그대로 |
networkd 설정을 찾아보니 유선 DHCP는 systemd 기본 파일이 하고 있었다.
# /usr/lib/systemd/network/80-wired.network (systemd-conf)
[Match]
Type=ether
Name=!veth*
...
[Network]
DHCP=yes
[DHCP]
RouteMetric=10
Type=ether는 “이더넷 형식의 인터페이스 전부”다. SPI로 붙은 PLC 모뎀도 리눅스에서는 이더넷 장치라 eth0도 DHCP를 시도하고 있었다. PLC는 충전 케이블 너머 차량과의 링크다. 그쪽에는 DHCP 서버가 없고, ISO 15118과 SLAC은 IPv6 링크 로컬 주소로 통신한다.
2-2. 설정 파일 세 개
networkd는 파일 이름 순서대로 보고 처음 맞는 파일 하나만 적용한다. 80-보다 앞 번호로 만들면 그 파일이 이긴다.
# 30-plc-qca7000.network
[Match]
Driver=qcaspi
[Link]
RequiredForOnline=no
[Network]
DHCP=no
LinkLocalAddressing=ipv6
IPv6AcceptRA=no
인터페이스 이름(eth0)이 아니라 드라이버 이름(qcaspi)으로 골랐다. 이름은 장치가 뜨는 순서에 따라 바뀔 수 있지만 드라이버는 바뀌지 않는다.
# 60-wlan.network
[Match]
Type=wlan
[Link]
RequiredForOnline=no
[Network]
DHCP=ipv4
[DHCPv4]
RouteMetric=600
유선과 WiFi가 같은 공유기에 물리면 둘 다 같은 대역(192.168.45.x)의 기본 경로를 갖는다. 우선순위가 같으면 응답이 엉뚱한 인터페이스로 나간다. 9월에 MYiR OS에서 WiFi ping이 실패했던 것도 이것 때문이었다. WiFi를 600으로 두면 둘 다 있을 때는 유선, 유선이 빠지면 WiFi로 나간다.
WiFi 접속 정보는 이미지에 넣지 않았다. 템플릿만 넣고, 보드에서 한 줄로 추가하는 방법을 주석으로 남겼다.
ctrl_interface=/var/run/wpa_supplicant
update_config=1
p2p_disabled=1
# 이미지에는 접속 정보(SSID/비밀번호)를 넣지 않는다. 보드에서 한 번 추가:
# wpa_passphrase "SSID" "비밀번호" | sed '/#psk=/d' >> /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
sed로 #psk= 줄을 지우는 이유는 wpa_passphrase가 평문 비밀번호를 주석으로 남기기 때문이다.
2-3. 템플릿 서비스를 켜는 방법
wpa_supplicant@wlan0을 부팅 때 켜려면 보통 레시피에 SYSTEMD_SERVICE를 쓴다. 하지만 systemd.bbclass는 서비스 파일이 자기 패키지 안에 있어야 한다. wpa_supplicant@.service는 wpa-supplicant 패키지 것이다. 그래서 systemctl enable이 만드는 것과 같은 링크를 직접 만들었다.
install -d ${D}${sysconfdir}/systemd/system/multi-user.target.wants
ln -sf ${systemd_system_unitdir}/wpa_supplicant@.service \
${D}${sysconfdir}/systemd/system/multi-user.target.wants/wpa_supplicant@wlan0.service
2-4. 보드에서 확인
이미지를 굽기 전에 .network 두 파일만 보드의 /etc/systemd/network/에 넣어 확인했다.
$ networkctl list
IDX LINK TYPE OPERATIONAL SETUP
2 end0 ether routable configured
3 eth0 ether degraded configured
4 wlan0 wlan routable configured
$ networkctl status eth0
Network File: /etc/systemd/network/30-plc-qca7000.network
Address: fe80::5cdd:1ff:fe0f:cd73
$ ip route | grep default
default via 192.168.45.1 dev end0 proto dhcp src 192.168.45.27 metric 10
default via 192.168.45.1 dev wlan0 proto dhcp src 192.168.45.88 metric 600
eth0의 degraded는 “링크 로컬 주소만 있음”이라는 뜻이고, PLC에서는 이게 정상이다.
처음에 확인할 때 wlan0 경로가 metric 10으로 나와서 잠깐 헷갈렸다. 그 줄에는 proto dhcp가 없었다. 아침에 손으로 실행한 udhcpc가 남긴 경로였다. 지우고 networkctl reconfigure를 하자 networkd가 만든 600짜리 경로로 바뀌었다.
3. MAC 주소 — PLC에는 MAC이 두 개다
3-1. 고정할 주소
STM32MP157의 OTP에 MAC이 비어 있어서, 유선 MAC은 부팅 구성에 따라 바뀌어 왔다. 개발용으로는 로컬 관리 주소를 고정했다.
02:4B:4E:56:00:xx
│ └──┬───┘
│ └ "KNV" (ASCII)
└ 첫 바이트의 두 번째 비트 = 로컬 관리 주소 (제조사 대역과 겹치지 않음)
| 주소 | 대상 | 설정하는 곳 |
|---|---|---|
:01 |
유선 end0 |
커널 DTS local-mac-address |
:02 |
PLC 호스트 쪽 인터페이스 eth0 |
커널 DTS local-mac-address |
:03 |
PLC 모뎀의 전력선 쪽 주소 | 모뎀 설정 파일(PIB) |
PLC 모뎀에는 MAC이 두 개 있다. 리눅스가 보는 SPI 인터페이스(eth0)의 주소와, 전력선 너머 차량이 보는 모뎀 자체의 주소다. 뒤의 것은 리눅스가 아니라 모뎀 펌웨어가 쓰는 값이라 PIB 파일 안에 들어 있다. 기본값은 Qualcomm의 00:B0:52:00:00:01이다. 충전기마다 같은 주소를 쓰면 안 된다.
3-2. DTS
ðernet0 {
...
local-mac-address = [02 4b 4e 56 00 01];
};
qca7000: ethernet@0 {
...
local-mac-address = [02 4b 4e 56 00 02];
};
U-Boot가 OTP에서 읽은 MAC을 DTB에 덮어쓰는 경우가 있어서 걱정했지만, OTP가 비어 있으니 그대로 들어왔다.
end0: link/ether 02:4b:4e:56:00:01
eth0: link/ether 02:4b:4e:56:00:02
3-3. PIB는 원본을 두고 사본을 고친다
open-plc-utils의 modpib로 PIB의 MAC을 바꿀 수 있다. 다만 QCA7005의 PIB 형식에서도 제대로 되는지 먼저 확인했다. 빌드 PC에서 modpib와 chkpib를 PC용으로 잠깐 빌드해 사본에 시험했다.
$ chkpib -v test.pib
MAC 00:B0:52:00:00:01
NMK 50:D3:E4:93:...
$ modpib -M 02:4B:4E:56:00:03 test.pib
$ chkpib -v test.pib
MAC 02:4B:4E:56:00:03 ← 체크섬 검사도 통과
적재 스크립트에서는 원본 PIB를 건드리지 않는다. 부팅할 때 /run에 사본을 만들어 고친 뒤 그걸 올린다. MAC은 설정 파일에서 읽는다. 나중에 보드마다 다른 값을 넣을 때 이 파일 하나만 바꾸면 된다.
CONF=/etc/default/qca7005-fw # QCA_PLC_MAC=02:4B:4E:56:00:03
[ -r "$CONF" ] && . "$CONF"
if [ -n "$QCA_PLC_MAC" ]; then
RUN_PIB=/run/qca7005-fw.pib
if cp "$PIB" "$RUN_PIB" && modpib -M "$QCA_PLC_MAC" "$RUN_PIB" >/dev/null; then
log "PIB MAC = $QCA_PLC_MAC"
PIB="$RUN_PIB"
else
log "modpib 실패 - 원본 PIB 의 MAC 으로 적재"
fi
fi
eth0 00:B0:52:00:00:01 Write /run/qca7005-fw.pib (1) (00200000:9796)
...
eth0 02:4B:4E:56:00:03 MAC-QCA7005-3.3.0.31-00-20231004-CS is running
펌웨어를 올리는 동안은 부트로더 기본 주소로 통신하고, 펌웨어가 올라온 순간부터 새 주소로 바뀐다. 참고로 이 PIB의 전력선 암호 키(NMK, DAK)도 Qualcomm 기본값이었다. 제품 단계의 보안 항목으로 적어 두었다.
3-4. 끝까지 실행하지 않고 “성공”한 스크립트
보드에 새 스크립트를 넣고 재부팅했는데 모뎀이 부트로더 상태로 남아 있었다. 서비스는 성공으로 끝났는데 로그가 이상했다.
01:32:25 PIB MAC = 02:4B:4E:56:00:03
01:32:25 eth0: Q_RST 하드웨어 리셋
01:32:27 Finished ← 2초 만에 성공, 적재 시도 없음
스크립트 안에는 리셋 직후 성공으로 끝나는 길이 없다. 파일을 확인해 보니 이랬다.
| 정상 | 보드 | |
|---|---|---|
| 크기 | 5017 바이트 | 4392 바이트 |
| 마지막 줄 | exit 1 |
주석 중간에서 끊김 |
4392바이트는 예전 스크립트의 크기였다. scp로 덮어쓴 직후 전원을 끄면서, 새 내용 일부만 디스크에 남은 것으로 보인다. 셸은 파일이 끝난 곳(리셋 직후)까지 실행하고 마지막 명령의 결과, 즉 성공으로 끝났다. 다시 복사하고 sync를 하니 정상으로 돌았다.
보드에 파일을 넣은 뒤에는 sync를 하거나 poweroff로 끄자. 그리고 “성공으로 끝났는데 결과가 이상하면” 실행된 파일부터 의심하자.
4. 블루투스 — 실패하면 스스로 다시 켜기
4-1. 남아 있던 문제
어제 재현 시험에서 블루투스는 7번 중 1번 완전히 실패했다. 드라이버는 칩 전원을 켜고 0.1초만 기다린 뒤 첫 명령을 보낸다. 칩이 늦으면 첫 명령이 타임아웃 나는데, 대부분은 다음 명령에서 회복한다. 회복하지 못하면 BD 주소가 00:00:00:00:00:00인 채로 남고 다시 시도하지 않는다.
4-2. 드라이버를 떼었다 붙이면 칩 전원부터 다시
어제 DTS에서 칩 전원 핀(PF15)을 드라이버에 넘겼다(shutdown-gpios). 그래서 드라이버를 떼었다 붙이면 칩 전원도 껐다 켜진다. 보드에서 직접 해 봤다.
echo serial0-0 > /sys/bus/serial/drivers/hci_uart_bcm/unbind
echo serial0-0 > /sys/bus/serial/drivers/hci_uart_bcm/bind
Bluetooth: hci0: command 0xfc18 tx timeout ← 이번에도 첫 명령은 놓침
Bluetooth: hci0: BCM: chip id 94
...
Bluetooth: hci0: BCM43430A1 (001.002.009) build 0264
다시 붙여도 hci0이라는 이름과 주소가 그대로였고, bluetoothd도 컨트롤러를 다시 인식했다. 칩 전원을 새로 켤 때마다 첫 명령을 놓친다는 것도 다시 확인됐다. 대기 시간이 짧다는 추정에 근거가 하나 더 생긴 셈이다.
4-3. 확인·복구 서비스
부팅 후 20초 안에 주소가 정상으로 들어오면 아무것도 하지 않는다. 끝내 00:00:...이면 다시 붙이기를 최대 3번 한다.
bt_ok() {
addr=$(hciconfig hci0 2>/dev/null | sed -n 's/.*BD Address: \([0-9A-F:]*\).*/\1/p')
[ -n "$addr" ] && [ "$addr" != "00:00:00:00:00:00" ]
}
실패를 일부러 만들 수는 없어서 시험용 스위치를 넣었다. BT_FORCE_RECOVER=1이면 정상이어도 복구 경로를 한 번 실행한다.
$ /tmp/sc6-bt-check.sh
sc6-bt-check: 정상 - hci0 40:FD:F3:EC:C5:79
$ BT_FORCE_RECOVER=1 /tmp/sc6-bt-check.sh
sc6-bt-check: hci0 초기화 실패 (주소 '없음') - serial0-0 다시 붙이기 1/3
...
sc6-bt-check: 복구 - hci0 40:FD:F3:EC:C5:79
근본 대책은 드라이버의 대기 시간을 늘리는 커널 패치다. 일단은 이 서비스가 완전 실패를 막아 주니, 패치는 필요해지면 검토한다.
5. 하지 않기로 한 것: dwc2 전원 경고
부팅할 때마다 USB OTG 컨트롤러가 이런 경고를 낸다.
dwc2 49000000.usb-otg: supply vusb_d not found, using dummy regulator
dwc2 49000000.usb-otg: supply vusb_a not found, using dummy regulator
정리하려고 ST 공식 보드(DK1)의 DTS를 봤더니 DK1에도 이 두 전원 연결이 없었다. 동작에 영향이 없는 경고다. 레퍼런스 보드와 다르게 만들면 오히려 나중에 비교하기 어려워진다. 그래서 그대로 두었다.
6. 정리
커밋
e5c5530 Linux 6.6 M6: 시스템 설정(호스트명/시간대/네트워크/MAC/BT 복구)
9f1ebb7 Linux 6.6 M6: 보드 펌웨어/서비스/PLC 도구 레시피 추가
오늘 배운 것
- 이상한 값에는 그 값을 만드는 코드가 있다. 긴 호스트 이름은 버그가 아니라 ST 서비스가 시키는 대로 MAC을 두 번 붙인 결과였다.
- “전부”에 맞는 기본 설정을 조심하라. systemd의
Type=ether는 SPI 위의 PLC 모뎀까지 잡았다. 장치를 고를 때는 이름보다 드라이버(Driver=)가 안정적이다. - 같은 이름의 장치라도 주소는 둘일 수 있다. PLC 모뎀은 호스트 쪽 MAC(DTS)과 전력선 쪽 MAC(PIB)이 따로다.
- 원본은 두고 사본을 고쳐라. PIB를
/run에서 고치면 원본 파일과 패키지는 그대로고, 설정 파일 하나로 값을 바꿀 수 있다. - 파일을 넣었으면
sync. 성공으로 끝난 서비스가 이상하면 실행된 파일의 크기와 md5부터 보자. - 실패를 만들 수 없으면 실패 경로를 강제로 탈 스위치를 만들어라.
BT_FORCE_RECOVER=1덕분에 복구 코드를 실제 보드에서 확인할 수 있었다.
다음 할 일 (M6 3단계)
- 충전기용 이미지 레시피:
st-image-core기반,st-hostname제외, EVerest를 빌드 PClocal.conf에서 레이어로 - 브링업 흔적 정리: TF-A 초기 콘솔과 로그 레벨, U-Boot 경고, 쓰지 않는 머신 기능
- 전체 빌드 → 손으로 넣은 것이 하나도 없는 새 카드 → M5 시험 전부 다시
댓글남기기