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 축을 기준으로 디버그합니다.

번호가 흔들리는 흔한 원인(보드 실측이 아님):

  1. I2C/SPI/USB GPIO 확장기가 늦게 probe되어 SoC 칩보다 앞·뒤로 끼어듦.
  2. 모듈 로드·DT overlay 적용 순서가 이미지·커널 설정마다 다름.
  3. 칩 개수 자체 변경(새 expander, 비활성 컨트롤러 제거).
  4. 앱이 하드코드한 /dev/gpiochip0 + offset만으로 배포되어, 맵이 바뀌어도 설정을 다시 안 읽음.

한 줄: 숫자는 열거 결과이고, 컨트롤러 정체는 name/label(과 필요 시 udev·경로)으로 확인합니다.

label·이름으로 고정하려면?

한 줄 답: libgpiod CLI·API는 칩을 번호·이름·경로로 받습니다. gpiodetect로 label·라인 수를 보고, 가능하면 label이 맞는 칩 또는 /dev/gpiochip*를 가리키는 안정 심볼릭 링크를 쓰고, 라인은 이름(있으면) → 그 칩의 offset 순으로 고릅니다.

실무 고정 순서:

  1. 목록 — gpiodetect로 gpiochipN [label] (N lines) 형태를 확인합니다. 인자를 생략하면 전 칩을 나열합니다.
  2. 라벨 대조 — 기대한 컨트롤러 label(또는 name)이 어느 N에 붙었는지 표로 남깁니다. N만 메모하지 않습니다.
  3. 라인 확인 — gpioinfo <chip>으로 라인 offset·name·consumer·사용 여부를 봅니다. 라인 이름이 있으면 앱·스크립트는 이름 우선.
  4. 열기 방식 — 설정 파일·환경 변수에 0만 넣지 말고, 칩 path/name 또는 label로 해석한 뒤 open하도록 둡니다. libgpiod는 0, gpiochip0, /dev/gpiochip0을 같은 칩 표기로 받을 수 있지만, 그 표기가 가리키는 하드웨어는 부팅마다 바뀔 수 있습니다.
  5. udev(선택) — 특정 부모 장치·속성에 맞춰 /dev/gpiochip*에 고정 심볼릭 링크를 만들면, 앱은 링크 경로만 열면 됩니다. 규칙은 배포 이미지에 포함하고, 숫자 하드코드는 제거합니다.
  6. 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을 혼동

확인 순서 (권장)

  1. gpiodetect — 칩 개수·각 label·lines. 배포 메모의 “기대 label”과 표 대조.
  2. 기대 label이 어느 path인지 — 숫자가 아니라 label/name으로 매핑 표를 갱신.
  3. gpioinfo on that chip — 쓰려는 라인 name·offset·USED/consumer. 이미 커널 드라이버가 잡고 있으면 userspace bitbang이 아닙니다(커널 chardev 문서의 “Do NOT abuse…”).
  4. 앱 설정 — 실제로 open한 문자열(0 / gpiochip0 / /dev/gpiochip0 / 커스텀 링크). 로그에 path·label을 남겨 재현을 고정.
  5. /sys/bus/gpio/devices/ — label·ngpio로 CLI와 교차. class gpio의 옛 base 번호와 섞지 않음.
  6. 권한 — /dev/gpiochip*의 group/mode(udev). open 실패를 “번호 밀림”으로만 단정하지 않음.
  7. 재부팅·모듈 reload 전후 gpiodetect diff — 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_info name/label/lines, line offset·name
  • libgpiod — GPIO tools — gpiodetect / gpioinfo, 칩 식별(번호·이름·경로)
  • /sys/bus/gpio/ — gpiochip 장치·label 등 sysfs 교차 확인
  • 인접 축: libgpiod(요청·CLI), sysfs-gpio-vs-libgpiod(선택), gpiomon-edge(엣지)