<zephyr/drivers/can.h>, ISO-TP API는 <zephyr/canbus/isotp.h>를 뜻한다. 기존 Networking 글의 IP·socket 흐름과 달리 CAN controller의 직접 프레임 API와 ISO-TP context에 초점을 둔다. 그림은 원본 코드의 경계를 새로 구성한 설명도이며 특정 보드의 측정 결과가 아니다.1. CAN ID로 고르는 프레임, ISO-TP 주소로 조립하는 메시지
struct can_frame은 표준 11비트 또는 확장 29비트 ID, DLC, flags와 payload를 담는다. 클래식 CAN의 프레임 payload는 최대 8바이트이고, CAN FD는 빌드 설정과 controller 지원이 함께 있을 때 최대 64바이트다. CAN 필터는 ID·mask·IDE 조건으로 개별 수신 프레임을 고른다. ISO-TP는 그 프레임 payload 안에 PCI를 두고 Single Frame(SF) 또는 First Frame(FF)·Consecutive Frame(CF)·Flow Control(FC)로 한 메시지를 주고받는다.
↔ 좁은 화면에서는 왼쪽 애플리케이션에서 오른쪽 버스까지 이동하세요.
애플리케이션
의미 있는 한 payload를 보내거나 읽는다.
직접 CAN: 한can_frame씩 다룸ISO-TP: isotp_send / isotp_recv로 메시지 다룸ISO-TP · 선택 계층
길이에 따라 SF 또는 FF·CF로 분할하고, 수신 측 FC의 BS·STmin을 따른다.
RX/TX 주소 쌍과 context 유지필요한 CAN 필터를 내부에서 설치CAN controller + 버스
한 프레임의 송신 완료와 ID·mask 수신 필터를 처리한다.
can_send → TX mailbox → 버스버스 → RX filter → callback 또는 msgq| 질문 | CAN controller 직접 API | ISO-TP |
|---|---|---|
| 애플리케이션의 단위 | 한 can_frame | 분할될 수 있는 한 payload |
| 수신 선택 | can_filter의 ID·mask·IDE | RX/TX 주소 쌍의 context가 필요한 filter를 설치 |
| 긴 payload | 호출자가 프레임 단위 정책을 직접 마련 | SF 또는 FF/FC/CF 상태 기계가 분할·조립 |
| 완료의 의미 | 프레임 전송/오류 | ISO-TP 상태 기계의 완료/오류. v3.7.0 하위 CAN TX 오류는 빠질 수 있음 |
2. can_send()의 timeout은 전송 전체 시간이 아니다
device가 준비됐는지 확인하고 controller를 시작한 뒤 프레임을 보낸다. can_send(dev, &frame, timeout, callback, user_data)의 timeout은 빈 TX mailbox를 기다리는 시간이다. callback을 주면 0 반환은 전송 완료가 아니라 큐잉 성공이며, 완료 또는 오류는 callback의 error를 본다. callback에 NULL을 주면 호출이 프레임 전송 완료까지 기다린다. 사용자 모드에서 이 API를 부를 때는 callback이 NULL이어야 한다.
↔ 좁은 화면에서는 송신 준비부터 결과까지 오른쪽으로 이동하세요. 아래 점선은 별도로 들어오는 수신 프레임입니다.
device_is_readycan_start 결과와 물리 transceiver 구성을 확인can_sendtimeout은 여기까지. 0 반환은 비동기 호출의 전송 완료가 아님ID + DLC + data상대 ACK, 중재, bus-off 등 실제 버스 조건이 영향callback(error)버스 결과 뒤 완료·오류 통지. NULL이면 호출이 완료까지 대기같은 ID의 순서. v3.7.0 헤더는 동일 우선순위 프레임의 큐잉 순서를 하드웨어 의존으로 둔다. 여러 CAN 프레임으로 자체 분할하면서 FIFO를 기대한다면 한 번에 하나씩 큐잉하도록 동기화하거나 ISO-TP를 사용한다. CAN_MODE_ONE_SHOT 및 FD 등 지원 mode는 controller마다 다르므로 can_get_capabilities()와 실제 드라이버를 확인한다.
근거: v3.7.0 can_send 계약·순서·사용자 모드, controller 시작·mode·capabilities.
3. 필터 callback과 msgq는 받은 프레임을 다루는 장소가 다르다
can_add_rx_filter()는 일치한 프레임을 callback으로 건네며, 이 callback은 인터럽트 문맥에서 실행된다. 여기서 블로킹 작업을 하지 말고 필요한 데이터를 복사하거나 다른 실행 흐름으로 넘긴다. can_add_rx_filter_msgq()는 초기화된 k_msgq에 프레임을 넣어 thread에서 꺼내게 한다. 다만 큐가 넘치면 이 wrapper는 버린 프레임을 조용히 무시하므로 손실을 세려면 직접 callback과 k_msgq_put() 결과를 조합한다. 두 방식 모두 성공 반환값은 filter ID이며 해제할 때 can_remove_rx_filter()에 사용한다.
| 경로 | 실행 위치 | 프레임을 오래 쓸 때 | 놓치기 쉬운 조건 |
|---|---|---|---|
can_add_rx_filter | RX callback은 ISR 문맥 | callback 안에서 필요한 값 복사·전달 | 블로킹 금지; 중복 필터 매치 우선순위는 하드웨어 의존 |
can_add_rx_filter_msgq | 내부 callback이 큐에 넣고 thread가 k_msgq_get | 꺼낸 can_frame 값 사용 | 큐 초과 프레임은 경고 없이 버려짐 |
예를 들어 11비트 ID 0x123을 정확히 고르면 .id=0x123, .mask=CAN_STD_ID_MASK, .flags=0이다. 확장 ID는 CAN_FILTER_IDE와 CAN_EXT_ID_MASK를 함께 사용한다. 필터 수가 없으면 -ENOSPC, 유형이 맞지 않거나 지원되지 않으면 해당 오류를 처리한다.
4. ISO-TP는 수신자의 FC를 기다리며 긴 메시지를 나눈다
클래식 CAN의 일반 주소 형식에서 7바이트 이하 payload는 SF 한 장으로 갈 수 있다. 그보다 큰 payload는 FF에 전체 길이와 앞부분을 넣고, 수신자가 FC로 허용 여부·block size(BS)·최소 프레임 간격(STmin)을 보낸 뒤 CF를 이어 보낸다. 주소 형식과 CAN FD 프레임 길이에 따라 SF의 실제 데이터 여유는 바뀌므로 “모든 ISO-TP 메시지의 SF 한계가 7바이트”라고 일반화하지 않는다.
↔ 좁은 화면에서는 ①부터 ⑥까지 오른쪽으로 이동하세요.
수신 측 isotp_bind수신 filter/context 등록; 송신 측은 data/context 확보isotp_send + FF전체 길이와 첫 data 전달FF 수신길이를 확인하고 후속 CF용 버퍼 확보FC: BS, STmin받을 수 있는 CF 개수와 최소 간격 지정SN + data순번을 확인하며 block 단위로 전송isotp_recv수신 버퍼 처리; 송신 ISO-TP 상태 확인isotp_bind()는 수신 context에 RX/TX 주소 조합을 묶고 CAN filter를 설치한다. 이 함수의 timeout은 첫 FF/SF 수신 버퍼 할당을 기다리는 시간이다. isotp_recv()는 context FIFO에서 호출자 버퍼로 복사하며 timeout 또는 프로토콜 오류를 음수 값으로 돌려준다. isotp_send()에 완료 callback을 주면 비동기로 실행되고, NULL이면 완료까지 기다린다. ISO-TP의 결과 코드는 CAN controller의 음수 errno와 다른 ISOTP_N_* 범주이므로 그대로 동일하게 해석하지 않는다.
v3.7.0 구현상 완료의 한계. isotp.c의 send_can_tx_cb(dev, error, arg)는 전달받은 CAN 송신 error를 검사하지 않고 backlog와 작업 상태만 진행한다. 따라서 ISOTP_N_OK만으로 하위 CAN 프레임 송신·ACK 성공을 입증할 수 없다. 버스 문제를 분리할 때는 controller 상태와 오류 카운터를 확인하고, 같은 장치에서 직접 can_send()로 프레임 경로를 별도 시험한다.
5. callback의 완료와 버퍼 소유권은 따로 추적한다
비동기 isotp_send()에 넘긴 data와 isotp_send_ctx는 전체 전송이 끝날 때까지 유효해야 한다. isotp_bind()의 isotp_recv_ctx도 isotp_unbind() 전까지 유지한다. isotp_recv()는 호출자 버퍼에 복사한다. 반면 isotp_recv_net()으로 받은 net_buf는 사용 후 net_buf_unref()해야 한다. BS가 0이 아닌 수신은 block 단위로 조각을 건넬 수 있으므로 반환값과 뒤따르는 수신을 함께 확인한다.
| API·객체 | 무엇을 확인하나 | 수명·해제 |
|---|---|---|
can_send + callback | 즉시 반환값은 큐잉, callback의 error는 프레임 결과 | callback 인자에 넘긴 애플리케이션 상태는 완료 관찰까지 유지 |
CAN RX callback의 frame* | 필터와 ID, DLC, payload | callback 뒤 포인터 보존을 가정하지 않고 필요한 값 복사 |
isotp_send + callback | ISOTP_N_*는 ISO-TP 상태 결과; v3.7.0에서 하위 CAN TX 오류를 모두 반영하지 않음 | data와 send context를 완료까지 유지 |
isotp_recv | 복사한 바이트 수 또는 음수 오류 | 호출자 버퍼 소유 |
isotp_recv_net | block 단위 수신과 남은 길이 | 받은 net_buf를 net_buf_unref |
수신 필터와 부분 읽기. 직접 등록한 filter는 filter ID를 보관했다가 can_remove_rx_filter()로 제거한다. isotp_unbind()는 자신이 설치한 filter, FIFO에 남은 버퍼와 rctx->buf를 정리한다. 다만 v3.7.0 구현은 isotp_recv()의 부분 읽기로 남은 rctx->recv_buf를 이 함수에서 해제하지 않는다. 따라서 중간에 수신을 버리는 경로까지 자동 정리된다고 단정하지 말고, 종료 전에 수신 처리와 동기화하고 부분 읽기 버퍼의 소진 여부를 확인해야 한다. 직접 필터와 ISO-TP 필터를 같은 ID로 임의 해제하지 않는다.
6. 실제 보드에서는 프레임 경로부터 확인한다
controller가 존재해도 device 준비 상태, transceiver, bus 배선과 bitrate, 상대 ACK 조건이 맞지 않으면 전송은 성립하지 않는다. DT_CHOSEN(zephyr_canbus)는 그 경로로 device를 선택하는 애플리케이션에서만 필요하다. 이미 유효한 dev를 직접 넘기는 can_send(dev, ...) 자체의 필수 조건은 아니다. CAN FD도 CONFIG_CAN_FD_MODE 하나만으로 모든 controller가 지원하는 것은 아니며 mode·timing·상대 노드를 함께 확인해야 한다. 아래 순서로 controller 계층을 먼저 검증한 뒤 ISO-TP context와 흐름 제어를 본다.
zephyr.dts의 CAN 노드와 .config 옵션, device_is_ready()를 확인한다. 앱이 DT_CHOSEN(zephyr_canbus)를 쓰면 chosen도 확인한다.
can_start(), 한 프레임 can_send(), 즉시 반환과 완료 callback 또는 blocking 결과를 분리해 기록한다.
표준/확장 ID와 mask, filter ID 또는 -ENOSPC, msgq 깊이와 손실 여부를 확인한다.
양쪽 RX/TX 주소와 FC 방향, BS·STmin, buffer pool, timeout 코드를 각각 기록한다.
| 관찰한 신호 | 먼저 볼 계층 | 확인할 사실 |
|---|---|---|
can_send == -EAGAIN | CAN controller | TX mailbox 대기 timeout; 버스 전체 timeout으로 단정하지 않기 |
-ENETDOWN / -ENETUNREACH | CAN controller | 각각 stopped / bus-off 상태를 can_get_state()로 확인 |
RX filter -ENOSPC | CAN controller | 필터 슬롯과 중복 직접/ISO-TP 등록 확인 |
ISO-TP ISOTP_N_TIMEOUT_BS / _CR | ISO-TP | FC 또는 후속 CF 수신 방향·상대 설정 확인 |
이 글은 v3.7.0의 API·구현을 읽어 만든 설계 지도다. 특정 SoC의 CAN controller를 빌드하거나 두 보드로 실제 ISO-TP 왕복을 측정한 결과는 포함하지 않는다.
7. Zephyr v3.7.0 고정 근거와 이어 읽기
API 명칭과 반환 의미는 v3.7.0 태그 및 같은 버전 공식 문서에 맞췄다. 최신 개발판의 추가 callback API나 controller 기능을 이 글에 소급하지 않았다.
| 근거 | 확인한 범위 |
|---|---|
| v3.7.0 can.h | frame/filter, send timeout·callback, RX ISR·msgq, state/recovery·FD 가드 |
| v3.7.0 ISO-TP 문서 | SF/FF/CF/FC, bind/send/recv/recv_net 수명·반환값 |
| v3.7.0 isotp.c | 수신 상태 기계와 buffer pool·FC 전송 위치 |