L2CAP: 채널, 신호, 크레딧 기반 흐름 제어
HCI의 ACL 데이터(2장) 위, ATT와 SMP 아래에 L2CAP이 있다. 하는 일은 셋이다. 한 연결 위에 여러 “채널”을 나눠 싣고, 채널을 열고 닫는 신호를 주고받고, 큰 데이터(SDU)를 작은 조각으로 나눠 상대가 받을 수 있는 만큼만 보낸다. 이 장은 LE의 L2CAP을 따라가며, 파일 하나가 SDU → K-frame → HCI ACL 조각 → LL PDU로 쪼개지는 과정을 바이트 단위로 계산한다.
- LE의 고정 채널과 동적 채널, L2CAP 기본 머리말과 신호 명령의 형식을 안다.
- LE 크레딧 기반 흐름 제어의 K-frame, MTU·MPS, 크레딧 규칙과 위반 시 동작을 설명할 수 있다.
- SDU 하나가 각 층에서 몇 조각이 되고 오버헤드가 얼마인지 계산할 수 있다.
- ECFC(여러 채널을 한 번에 여는 방식)와 재설정 규칙, EATT와의 관계를 안다.
채널과 CID
L2CAP 패킷은 4바이트 머리말(길이 2바이트 + CID 2바이트)로 시작한다. CID는 “이 연결 안에서 어느 채널로 가는가”다. LE 연결에서는 몇몇 채널이 연결이 생기자마자 열려 있다.
| CID | LE-U에서의 쓰임 |
|---|---|
| 0x0004 | ATT(11장, 1권 7장) |
| 0x0005 | LE 신호 채널 |
| 0x0006 | SMP(7장) |
| 0x0020~0x003E | 할당 번호 문서가 정함 |
| 0x0040~0x007F | 동적 채널(크레딧 기반 연결로 열고 닫음) |
고정 채널은 양쪽 CID가 같지만, 동적 채널은 각자 자기 쪽 CID를 정해 알려 준다(내가 받을 CID = 소스 CID). 그래서 같은 채널이라도 방향마다 CID가 다를 수 있다.
연결 하나는 트럭 한 대고, CID는 화물칸 번호다. 0x0004번 칸은 늘 서류(ATT), 0x0006번은 늘 열쇠(SMP)를 싣는다. 큰 짐(파일 전송, 오디오 설정)을 실으려면 신호 칸(0x0005)으로 “새 칸 하나 붙이자”고 합의해 0x0040번 이후 칸을 연다. 받는 쪽이 하역을 끝낸 만큼만 다음 짐을 싣는 것이 크레딧이다.
CID와 LE-U 이름 공간(Table 2.3): [Vol 3] Part A §2.1 · 기본 머리말: §3.1
신호 명령
신호 채널의 L2CAP 페이로드(C-frame)는 코드(1) · 식별자(1) · 길이(2) · 데이터다. 식별자로 요청과 응답을 짝짓는다. LE 신호 채널(0x0005)에는 C-frame 하나에 명령 하나만 싣는다(클래식 0x0001은 여러 개 가능). 받은 명령을 모르면 L2CAP_COMMAND_REJECT_RSP(0x01)로 답한다.
| 코드 | 명령 | 쓰임 |
|---|---|---|
| 0x06 / 0x07 | DISCONNECTION_REQ / RSP | 동적 채널 닫기 |
| 0x12 / 0x13 | CONNECTION_PARAMETER_UPDATE_REQ / RSP | 페리페럴 호스트가 연결 파라미터를 요청. LL의 연결 파라미터 요청 절차(4장)를 쓸 수 없을 때만 |
| 0x14 / 0x15 | LE_CREDIT_BASED_CONNECTION_REQ / RSP | LE 크레딧 기반 채널 하나 열기 |
| 0x16 | FLOW_CONTROL_CREDIT_IND | 크레딧 돌려주기 |
| 0x17 / 0x18 | CREDIT_BASED_CONNECTION_REQ / RSP | ECFC 채널 최대 5개 한 번에 열기 |
| 0x19 / 0x1A | CREDIT_BASED_RECONFIGURE_REQ / RSP | ECFC 채널의 MTU·MPS 바꾸기 |
채널을 열 때는 SPSM(서비스 번호)을 준다. 0x0001~0x007F는 SIG가 할당한 프로토콜(EATT 등), 0x0080~0x00FF는 동적 번호로, 클라이언트가 GATT에서 매번 다시 읽어야 한다.
신호 패킷 형식과 LE 한 명령 규칙: [Vol 3] Part A §4 · 명령: §4.1~§4.28 · 연결 파라미터 갱신은 페리페럴만, LL 절차를 못 쓸 때만: §4.20 · SPSM 범위(Table 4.15): §4.22
LE 크레딧 기반 흐름 제어
동적 채널의 데이터 단위는 K-frame이다. 상위 계층의 데이터 덩어리(SDU)를 K-frame 여러 개로 나누고, 첫 K-frame에만 SDU 전체 길이(2바이트)를 붙인다. 두 숫자가 크기를 정한다.
- MTU: 내가 받을 수 있는 가장 큰 SDU. LE 크레딧 기반은 최소 23바이트.
- MPS: 내가 받을 수 있는 가장 큰 K-frame 페이로드. 최소 23, 최대 65533.
그리고 크레딧. 채널을 열 때 서로 “처음에 K-frame을 몇 개까지 보내도 된다”(0~65535)를 주고, K-frame 하나를 보낼 때마다 하나씩 쓴다. 받는 쪽은 처리한 만큼 FLOW_CONTROL_CREDIT_IND로 돌려준다. 규칙은 엄격하다. 크레딧이 0인데 K-frame을 받으면, 크레딧 합이 65535를 넘으면, SDU가 MTU를 넘거나 K-frame이 MPS를 넘거나 조각 합이 SDU 길이를 넘으면 받는 쪽이 채널을 끊는다. 크레딧 0을 돌려주는 IND는 무시한다(보내면 안 된다).
SDU 하나가 각 층에서 몇 조각이 되나
수신 앱이 느려서 크레딧을 늦게 돌려준다. 송신 쪽 크레딧이 0이 되었을 때, 송신 쪽 L2CAP은 무엇을 해야 할까?
크레딧 0에서 K-frame을 받으면 상대는 무엇을 하나?
K-frame과 SDU 길이·MTU·MPS 위반 시 끊기: [Vol 3] Part A §3.4 · MTU·MPS 최소 23, 초기 크레딧 0~65535: §4.22 · 크레딧 규칙(65535, 0 크레딧, 0 값 IND): §10.1 · 크레딧 IND: §4.24 · 조각내기: §7.2
ECFC: 채널 여럿을 한 번에, 그리고 EATT
5.2에서 더한 향상된 크레딧 기반 흐름 제어(ECFC)는 같은 크레딧 방식을 LE와 클래식 모두에 쓰고, 요청 하나로 채널을 최대 5개 연다. MTU와 MPS는 최소 64바이트다. 열린 뒤에도 RECONFIGURE로 MTU·MPS를 바꿀 수 있는데, MTU는 줄일 수 없고, 채널을 여럿 함께 바꿀 때는 MPS도 줄일 수 없다(이미 보낸 조각이 새 한도를 넘지 않게). 받는 쪽은 옛 MPS로 보내던 PDU를 다 보낸 뒤에야 응답한다.
ECFC의 대표 사용자가 EATT(Enhanced ATT)다. 0x0004 고정 채널 하나뿐이던 ATT를 ECFC 채널 여럿으로 늘려, 한 채널에서 긴 읽기가 진행 중이어도 다른 채널로 알림이나 다른 요청을 동시에 보낸다. EATT 채널은 암호화된 연결에서만 열고, K-frame 하나에 ATT PDU 하나만 싣는다(11장).
ECFC 재설정 요청 검사
ECFC 연결(최대 5채널, MTU·MPS ≥ 64): [Vol 3] Part A §4.25 · 재설정 규칙: §7.11·§4.27 · ECFC 흐름 제어: §10.2 · EATT 베어러 요구: [Vol 3] Part G §5.3
핵심 정리
- L2CAP 머리말 = 길이 2 + CID 2. LE 고정 채널: ATT 0x0004, 신호 0x0005, SMP 0x0006. 동적 채널 0x0040~0x007F.
- 신호 C-frame = 코드 · 식별자 · 길이 · 데이터. LE 신호 채널에는 명령 하나씩. 연결 파라미터 갱신 요청(0x12)은 LL 절차를 못 쓸 때 페리페럴만.
- K-frame: 첫 조각에만 SDU 길이. MTU·MPS ≥ 23(LE 크레딧 기반), ≥ 64(ECFC). K-frame 하나 = 크레딧 하나.
- 크레딧 0에서 받음, 65535 초과, MTU·MPS·SDU 길이 위반이면 받는 쪽이 채널을 끊는다.
- ECFC는 한 번에 최대 5채널, 재설정으로 MTU는 늘리기만, 여러 채널이면 MPS도 줄이지 못한다. EATT가 이 채널들을 쓴다.
확인 퀴즈
LE에서 SMP가 쓰는 L2CAP CID는?
SDU 600바이트를 MPS 200으로 보내면 K-frame은 몇 개인가?
크레딧 IND의 Credits 값으로 보낼 수 없는 것은?
ECFC 재설정에서 항상 금지되는 것은?