Chapter 03

링크 계층 상태 기계와 컨트롤러 스케줄링

스마트워치 하나가 휴대폰과 연결을 유지하면서, 다른 휴대폰에게 광고하고, 체중계를 스캔하고, 이어폰 방송을 받는다. 그런데 무선은 하나다. 사양은 “무엇을 언제 해야 하는가”를 정하고, “겹치면 무엇을 먼저 하는가”는 컨트롤러 구현에 맡긴다. 이 장은 그 경계를 따라간다. 연결 이벤트를 어떻게 세고 건너뛰는지는 사양대로 정확히 계산하고, 스케줄러는 사양의 제약을 지키는 예시 모델로 만들어 본다.

일곱 상태, 여러 개의 상태 기계

링크 계층 상태 기계 하나는 한 번에 한 상태에만 있다. 대기(Standby), 광고, 스캔, 연결 시작(Initiating), 연결(센트럴 또는 페리페럴 역할), 동기화(주기적 광고·BIG 받기), 등시성 방송. 모든 상태는 대기에서 들어가고, 연결 상태만 예외로 연결 시작(→ 센트럴)이나 광고(→ 페리페럴)에서 들어간다. 그리고 링크 계층은 상태 기계를 여러 개 가질 수 있다. 위의 스마트워치는 상태 기계 넷이 동시에 돌고 있는 것이다.

대기 광고 연결 시작 연결 스캔 동기화 등시성 방송 CONNECT_IND 받음 → 페리페럴 CONNECT_IND 보냄 → 센트럴
그림 3-1. 상태 기계 하나의 상태와 전이. 어느 상태에서든 대기로 돌아갈 수 있다(점선은 그 가운데 하나만 그림). 실제 컨트롤러는 이런 상태 기계를 여러 개 돌린다.

모든 조합을 지원할 의무는 없지만 짝을 이룬 의무가 있다. 연결 시작 상태(+ 다른 상태 C)를 지원하면 같은 C와 함께 센트럴 연결도 지원해야 하고, 센트럴 연결 + 연결 시작을 지원하면 페리페럴 여럿과의 센트럴 연결도 지원해야 한다. 광고 쪽도 대칭이다. 연결을 “만들 수 있는” 조합이면 만든 뒤의 조합도 감당해야 한다는 뜻이다.

일곱 상태와 전이(그림 1.1): [Vol 6] Part B §1.1 · 상태 기계 여러 개와 조합 의무(Table 1.1): §1.1.1

연결 이벤트 하나의 시간

연결 이벤트의 시작이 앵커 포인트다. 센트럴은 보낼 것이 있으면 앵커 포인트에 정확히 첫 PDU를 보내고, 페리페럴은 그 시각에 듣는다. 이벤트 안에서는 센트럴 → 페리페럴 사이에 T_IFS_ACL_CP, 페리페럴 → 센트럴 사이에 T_IFS_ACL_PC(기본 150 µs)를 두고 번갈아 보내며, 한 이벤트는 한 채널만 쓴다. 이벤트가 끝나는 조건(MD 비트, CRC 오류 연속)은 1권 6장에서 봤다. 센트럴은 마지막 패킷과 다음 앵커 포인트 사이에 최소 T_MCES(기본 150 µs)를 남겨야 한다. 앵커 포인트는 connInterval마다 오고 서로 겹치지 않는다.

두 기기는 connEventCounter(16비트)를 함께 센다. 첫 이벤트가 0이고, 이벤트마다 1씩 늘며 0xFFFF 다음 0으로 돌아간다. 중요한 것은 건너뛴 이벤트도 센다는 점이다. 센트럴이 아무것도 보내지 않았어도, 페리페럴이 듣지 않았어도 카운터는 오른다. 다음 장의 “instant”가 이 카운터로 약속하기 때문에, 둘이 서로 다른 이벤트 수를 셌다면 절차가 어긋난다.

매시 정각의 통화, 받지 않은 시각도 센다

“매일 정각마다 통화하고, 오늘부터 23번째 통화에서 새 번호로 바꾸자”고 약속했다면, 내가 바빠서 받지 못한 정각도 세어야 둘의 23번째가 같은 시각이 된다. 그래서 링크 계층은 건너뛴 이벤트도 빠짐없이 센다.

앵커 포인트·connEventCounter·T_MCES 규칙: [Vol 6] Part B §4.5.1 · T_IFS: §4.1.1 · T_MCES 기본 150 µs: §4.1.5 · 이벤트 닫기: §4.5.6

누가 어느 이벤트에 나오나: 서브레이팅·연속 이벤트·지연

connInterval이 짧으면 반응은 빠르지만 전력이 든다. 연결을 끊지 않고 이벤트를 줄이는 장치가 셋 있다.

제약은 factor × (latency + 1) ≤ 500, connInterval × factor × (latency + 1) < 감독 타임아웃의 절반이다. 사양의 예: factor 3, continuation 0, latency 6이면 페리페럴은 21번째 이벤트마다(7번째 S마다) 들으면 된다.

SIMULATOR

이벤트 지도: S·C·L

채움 = 서브레이트 이벤트(S) · 다른 색 = 연속 이벤트(C) · 점선 = 페리페럴이 지연을 써서 듣지 않는 S(L) · 아래 점 = 그 이벤트에 길이가 0이 아닌 PDU가 오감 · 회색 = 쓰지 않는 이벤트
데이터
센트럴이 쓰는 이벤트—
페리페럴이 듣는 이벤트—
factor × (latency + 1)—
이벤트 0~47을 보인다. 해볼 것: ① 처음 값(3, 0, 0, 6)은 사양의 예다. 페리페럴은 0, 21, 42번만 듣는다. ② base를 2로 바꾸면 S의 위치가 옮겨 간다(사양 그림 4.44). ③ latency 0, continuation 2, 데이터 “이벤트 9에 한 번”이면 9 뒤의 두 이벤트(10, 11)가 C가 되고 페리페럴은 그 C를 듣는다. latency를 다시 올려 9가 L(페리페럴이 듣지 않음)이 되면 데이터가 오가지 못해 C도 생기지 않는다. 센트럴은 다음 기회에 다시 보낸다. ④ factor 12, latency 10이면 12 × 11 = 132로 500 이하지만, connInterval × 132가 감독 타임아웃의 절반보다 작아야 한다는 조건도 따로 맞춰야 한다. 모델: 데이터는 그 이벤트에서 센트럴이 보냈고 페리페럴이 받았다고 가정한다.

서브레이트·연속 이벤트·페리페럴 지연의 정의와 예(21번째 이벤트), 제약: [Vol 6] Part B §4.5.1(그림 4.43~4.47) · 카운터가 넘칠 때 base 조정: 같은 절 · 서브레이팅 절차: §5.1.19·§5.1.20

무선 하나, 일 여럿: 스케줄러

센트럴의 연결 이벤트, 광고 이벤트, 스캔 창, 다른 연결의 이벤트가 같은 시각에 겹치면 컨트롤러는 하나만 할 수 있다. 사양은 어떤 것을 고르라고 정하지 않는다. 대신 경계를 정한다. 센트럴은 “일정 충돌로” 한 이벤트에서 아무것도 보내지 않을 수 있지만, 감독 타임아웃 안에 적어도 한 번은 보내야 한다. 페리페럴도 지연이나 충돌과 상관없이 감독 타임아웃 안에 적어도 한 번은 들어야 한다. 이 경계를 넘으면 상대 쪽 타이머가 끝나 연결이 끊기고 오류 코드 0x08(Connection Timeout)이 남는다.

그래서 실제 스케줄러는 대개 우선순위에 “굶은 정도”를 더한다. 오래 건너뛴 일일수록 다음 충돌에서 이기게 한다. 센트럴은 자기 연결들의 앵커 포인트를 처음부터 서로 엇갈리게 놓아 충돌 자체를 줄인다(연결 간격을 서로 배수로 맞추는 이유).

SIMULATOR

스케줄러 예시 모델: 1초 동안의 무선

충돌 규칙
A 건너뜀 / 가장 긴 공백—
B 건너뜀 / 가장 긴 공백—
광고 건너뜀—
행: 연결 A(이벤트 4 ms), 연결 B(이벤트 4 ms), 광고(100 ms마다 3 ms), 스캔(옅은 색, 다른 일에 밀려나는 배경 작업). 채움 = 실행, 빨간 테두리 = 건너뜀. 연결 간격은 1.25 ms 단위. 이것은 사양이 아니라 사양의 제약(겹치지 않음, 감독 타임아웃 안에 한 번)을 지키는 예시 모델이다. 고정 우선순위는 연결 A > B > 광고 > 스캔. 해볼 것: ① 고정 우선순위에서 A와 B 간격을 같게(예: 30 ms) 하면 B가 계속 진다. 감독 타임아웃이 짧으면 끊긴다. ② “굶은 만큼 올림”으로 바꾸면 번갈아 이긴다. ③ 스캔은 다른 일을 막지 않고 빈 시간에만 듣는다. 실제 스캐너가 연결 이벤트에 자리를 내주는 방식이다.
예측해 보기

센트럴이 연결 여섯 개를 7.5 ms 간격으로 유지하고, 각 연결이 매 이벤트에 2.5 ms씩 쓴다. 무엇이 일어날까?

이벤트 하나에 2.5 ms × 6 = ?

7.5 ms 안에 15 ms 분량이 필요하다. 아무리 엇갈려도 절반은 건너뛴다. 공정한 스케줄러라면 각 연결이 대략 두 번에 한 번 서비스받아 버틸 수 있지만, 스캔이나 광고까지 더하면 더 굶는다. 연결 간격을 늘리거나(연결 갱신 절차, 4장), 이벤트 길이(데이터 양)를 줄여야 한다.

일정 충돌로 이벤트에서 보내지 않음·감독 타임아웃 안에 한 번: [Vol 6] Part B §4.5.1 · 감독 타임아웃: §4.5.2 · 오류 코드 0x08: [Vol 1] Part F §2

핵심 정리

  1. 상태는 일곱, 상태 기계는 여럿. 연결을 만들 수 있는 조합을 지원하면 만든 뒤의 조합도 지원해야 한다(Table 1.1).
  2. 앵커 포인트에 센트럴이 먼저 보내고, 이벤트 사이에 T_MCES(기본 150 µs)를 남긴다. connEventCounter는 건너뛴 이벤트도 센다.
  3. S = (count − base)가 factor의 배수인 이벤트, C = 직전 continuation개 이벤트에 데이터가 있었던 이벤트, L = 페리페럴이 지연으로 건너뛰는 S. factor × (latency + 1) ≤ 500.
  4. 스케줄러는 구현의 몫이지만, 감독 타임아웃 안에 센트럴은 한 번 보내고 페리페럴은 한 번 들어야 한다. 어기면 0x08로 끊긴다.

확인 퀴즈

센트럴이 충돌로 한 연결 이벤트에서 아무것도 보내지 않았다. connEventCounter는?

[Vol 6] Part B §4.5.1: 보내지 않았거나 듣지 않은 이벤트도 양쪽 모두 센다.

factor 4, base 1이면 서브레이트 이벤트는?

(count − 1)이 4의 배수인 이벤트.

factor 3, continuation 0, latency 6에서 페리페럴이 반드시 들어야 하는 간격은?

3 × (6 + 1) = 21. 사양의 예 그대로다.

센트럴이 감독 타임아웃 동안 한 번도 보내지 못하면?

페리페럴의 감독 타이머가 끝나 연결을 잃은 것으로 본다.