gpiochip 번호가 바뀌었을 때 어떻게 디버그하나?
앱·스크립트가 /dev/gpiochip0만 열어 두었는데, 커널·모듈 로드 순서나 확장 칩이 늘어난 뒤 같은 숫자가 다른 컨트롤러를 가리키면 라인 요청이 실패하거나 엉뚱한 핀을 건드립니다. gpiochip 번호 디버그는 “숫자를 다시 외우기”가 아니라 번호가 어디서 오는지 보고, label·이름(또는 안정 경로)으로 고르는 절차입니다.
이 글은 **chip 번호는 어디서 오나? · label·이름으로 고정하려면? · 앱 깨짐 증상과 확인 순서?**만 다룹니다. sysfs vs libgpiod 선택(sysfs-gpio-vs-libgpiod), 라인 요청·CLI 본편(libgpiod), rising/falling(gpiomon-edge)은 별도입니다. 보드별 창작 타이밍·핀맵 후기·제휴·샘플 코드 덤프는 없습니다.
근거: GPIO character device의 gpiochip_info(name·label·lines), gpiodetect(칩을 번호·이름·경로로 식별), sysfs 버스 노드 /sys/bus/gpio/.
chip 번호는 어디서 오나?
한 줄 답: /dev/gpiochipX의 X는 문자 디바이스 열거 인덱스입니다. 커널 gpiochip_info.name은 그 칩의 커널 이름, label은 기능적 이름(비어 있을 수 있음)이고, 라인은 칩 안 offset([0, lines))으로 구분합니다. 부팅·모듈·핫플러그 순서에 따라 X가 밀릴 수 있으므로 “항상 0번 = SoC GPIO”를 계약으로 두면 안 됩니다.
정리하면:
| 식별자 | 의미 | 안정성 |
|---|---|---|
/dev/gpiochipX의 X | 문자 디바이스 노드 번호 | 열거 순서에 의존 — 칩이 늘거나 로드 순서가 바뀌면 밀림 |
gpiochip_info.name | 커널이 붙인 칩 이름 | 보통 노드 이름과 대응(gpiochip0 등). 번호만으로는 컨트롤러 종류를 보장하지 않음 |
gpiochip_info.label | 기능적 라벨(제품·드라이버 표기) | 컨트롤러를 가리키는 힌트. 비어 있을 수 있음 |
| 라인 offset | 해당 칩 안의 로컬 인덱스 | 칩이 같으면 offset 의미는 유지. 칩이 바뀌면 같은 offset도 다른 핀 |
라인 name | 보드/칩이 준 라인 이름 | 있으면 이름으로 고르는 편이 숫자보다 읽기 쉬움. 비어 있을 수 있음 |
/sys/bus/gpio/devices/ 아래에는 보통 gpiochipN 항목이 있고, label·ngpio 등 속성을 읽을 수 있습니다. 레거시 /sys/class/gpio/gpiochipN의 N은 역사적으로 전역 base에 가깝게 쓰인 경로가 있어, /dev/gpiochipN의 N과 숫자를 무조건 같다고 보지 마십시오. 신규 userspace는 character device + libgpiod 축을 기준으로 디버그합니다.
번호가 흔들리는 흔한 원인(보드 실측이 아님):
- I2C/SPI/USB GPIO 확장기가 늦게 probe되어 SoC 칩보다 앞·뒤로 끼어듦.
- 모듈 로드·DT overlay 적용 순서가 이미지·커널 설정마다 다름.
- 칩 개수 자체 변경(새 expander, 비활성 컨트롤러 제거).
- 앱이 하드코드한
/dev/gpiochip0+ offset만으로 배포되어, 맵이 바뀌어도 설정을 다시 안 읽음.
한 줄: 숫자는 열거 결과이고, 컨트롤러 정체는 name/label(과 필요 시 udev·경로)으로 확인합니다.
label·이름으로 고정하려면?
한 줄 답: libgpiod CLI·API는 칩을 번호·이름·경로로 받습니다. gpiodetect로 label·라인 수를 보고, 가능하면 label이 맞는 칩 또는 /dev/gpiochip*를 가리키는 안정 심볼릭 링크를 쓰고, 라인은 이름(있으면) → 그 칩의 offset 순으로 고릅니다.
실무 고정 순서:
- 목록 —
gpiodetect로gpiochipN [label] (N lines)형태를 확인합니다. 인자를 생략하면 전 칩을 나열합니다. - 라벨 대조 — 기대한 컨트롤러 label(또는 name)이 어느 N에 붙었는지 표로 남깁니다. N만 메모하지 않습니다.
- 라인 확인 —
gpioinfo <chip>으로 라인 offset·name·consumer·사용 여부를 봅니다. 라인 이름이 있으면 앱·스크립트는 이름 우선. - 열기 방식 — 설정 파일·환경 변수에
0만 넣지 말고, 칩 path/name 또는 label로 해석한 뒤 open하도록 둡니다. libgpiod는0,gpiochip0,/dev/gpiochip0을 같은 칩 표기로 받을 수 있지만, 그 표기가 가리키는 하드웨어는 부팅마다 바뀔 수 있습니다. - udev(선택) — 특정 부모 장치·속성에 맞춰
/dev/gpiochip*에 고정 심볼릭 링크를 만들면, 앱은 링크 경로만 열면 됩니다. 규칙은 배포 이미지에 포함하고, 숫자 하드코드는 제거합니다. - sysfs 교차 확인 —
/sys/bus/gpio/devices/gpiochipN/label(및 관련 속성)을gpiodetect결과와 맞춰, “지금 N이 누구인지”를 한 번 더 검증합니다.
# 칩 목록 (label · 라인 수)
gpiodetect
# 특정 칩의 라인 name / offset / consumer
gpioinfo gpiochip0
# 또는: gpioinfo /dev/gpiochip0
# 버스 쪽 label 교차 확인 (경로·속성명은 커널·보드에 따라 다를 수 있음)
ls /sys/bus/gpio/devices/
cat /sys/bus/gpio/devices/gpiochip0/label 2>/dev/null || true
하지 말 것: “우리 보드는 항상 chip2”처럼 발명한 고정 번호를 문서화하기. label이 비어 있으면 name·부모 장치·udev로 가고, 빈 label을 임의 문자열로 채운 척하지 않습니다.
앱 깨짐 증상과 확인 순서는?
한 줄 답: 증상은 대개 open 실패, 라인 요청 거부(이미 사용·잘못된 offset), 동작은 되나 다른 핀, 재부팅 후에만 재현입니다. 확인은 목록 → label 대조 → 라인 info → 앱이 연 path → (필요 시) 권한 순으로 좁힙니다.
증상 패턴
| 증상 | 의심 |
|---|---|
/dev/gpiochipN open 실패 | N이 없음(칩 개수 감소) 또는 권한 |
| open은 되나 request/get/set 실패 | 다른 칩의 offset을 씀 · 라인 USED · consumer 충돌 |
| 값은 바뀌나 하드웨어 무반응/다른 장치 반응 | 같은 N이 다른 컨트롤러를 가리킴 |
| 특정 이미지·커널만 깨짐 | 모듈/overlay 순서로 열거 밀림 |
레거시 sysfs export 전역 번호도 어긋남 | obsolete 전역 번호와 chardev N을 혼동 |
확인 순서 (권장)
gpiodetect— 칩 개수·각 label·lines. 배포 메모의 “기대 label”과 표 대조.- 기대 label이 어느 path인지 — 숫자가 아니라 label/name으로 매핑 표를 갱신.
gpioinfoon that chip — 쓰려는 라인 name·offset·USED/consumer. 이미 커널 드라이버가 잡고 있으면 userspace bitbang이 아닙니다(커널 chardev 문서의 “Do NOT abuse…”).- 앱 설정 — 실제로 open한 문자열(
0/gpiochip0//dev/gpiochip0/ 커스텀 링크). 로그에 path·label을 남겨 재현을 고정. /sys/bus/gpio/devices/— label·ngpio로 CLI와 교차. class gpio의 옛 base 번호와 섞지 않음.- 권한 —
/dev/gpiochip*의 group/mode(udev). open 실패를 “번호 밀림”으로만 단정하지 않음. - 재부팅·모듈 reload 전후
gpiodetectdiff — N만 바뀌고 label이 이동했는지 확인. 이동이면 앱을 label/링크 기준으로 수정.
Debug order:
1) gpiodetect → map N ↔ label
2) match expected label → choose path/name (not bare N in config)
3) gpioinfo <that chip> → line name / offset / consumer
4) app open() target → log path + resolved label
5) /sys/bus/gpio/devices/…/label → cross-check
6) permissions on /dev/gpiochip* → if open fails
7) before/after boot detect diff → confirm renumber vs wrong offset
한 줄 정리: 깨짐의 대부분은 숫자 계약을 하드웨어 계약으로 착각한 경우입니다. label(또는 안정 링크) + 라인 이름/offset으로 고르고, 디버그는 detect → info → 앱 path 순서를 지킵니다.
FAQ
/dev/gpiochip0과 /sys/class/gpio/gpiochip0은 같나요?
항상 같다고 가정하지 마십시오. character device의 N은 열거 인덱스이고, 레거시 class gpio의 gpiochip*는 base 등 다른 축으로 노출된 경로가 있습니다. 신규 디버그는 /dev/gpiochip* + libgpiod와 /sys/bus/gpio/ 교차 확인을 우선합니다.
label이 비어 있으면?
커널 문서상 label은 비어 있을 수 있습니다. 그때는 name, 부모 장치 속성, udev 심볼릭 링크, 또는 배포 시점에 gpiodetect로 만든 명시적 맵 파일(label 대신 name/path)을 씁니다. 빈 값을 임의로 채워 넣지 않습니다.
라인 offset만 바꾸면 되나요?
칩 N이 밀려 다른 컨트롤러가 되면 offset 의미도 바뀝니다. 먼저 칩 정체(label/name) 를 맞춘 뒤 offset·라인 이름을 봅니다.
sysfs export 전역 번호로 버티면?
obsolete 경로이며, 전역 번호도 보드/조합에 따라 흔들릴 수 있다고 커널이 경고합니다. 이 글의 축은 chardev 번호 밀림 디버그입니다. 선택 비교는 sysfs-gpio-vs-libgpiod를 보십시오.
출처 (Sources)
- GPIO Character Device Userspace API — Chip
/dev/gpiochipX,gpiochip_infoname/label/lines, line offset·name - libgpiod — GPIO tools —
gpiodetect/gpioinfo, 칩 식별(번호·이름·경로) /sys/bus/gpio/— gpiochip 장치·label 등 sysfs 교차 확인- 인접 축: libgpiod(요청·CLI), sysfs-gpio-vs-libgpiod(선택), gpiomon-edge(엣지)