Documentation/driver-api/nfc/nfc-hci.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

HCI backend for NFC Core

NFC HCI gate·pipe·PHY·LLC·worker·error model의 전문 번역입니다.

Source pathDocumentation/driver-api/nfc/nfc-hci.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

nfc-hci.rst:1-311

HCI backend는 NFC Core request를 gate 기반 command로 변환하고 PHY·LLC와 worker context를 분리해 synchronous·asynchronous 실행과 error 전파를 처리합니다.

문서 구성
원문 줄내용
1-52HCI·session·gate
53-147Driver와 PHY interface
148-222LLC와 worker
223-278Command·event workflow
279-311Error management

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 ========================
2 HCI backend for NFC Core
3 ========================
4
5 - Author: Eric Lapuyade, Samuel Ortiz
6 - Contact: eric.lapuyade@intel.com, samuel.ortiz@intel.com
7
8 General
9 -------
10
11 The HCI layer implements much of the ETSI TS 102 622 V10.2.0 specification. It
12 enables easy writing of HCI-based NFC drivers. The HCI layer runs as an NFC Core
13 backend, implementing an abstract nfc device and translating NFC Core API
14 to HCI commands and events.
15
16 HCI
17 ---
18
19 HCI registers as an nfc device with NFC Core. Requests coming from userspace are
20 routed through netlink sockets to NFC Core and then to HCI. From this point,
21 they are translated in a sequence of HCI commands sent to the HCI layer in the
22 host controller (the chip). Commands can be executed synchronously (the sending
23 context blocks waiting for response) or asynchronously (the response is returned
24 from HCI Rx context).
25 HCI events can also be received from the host controller. They will be handled
26 and a translation will be forwarded to NFC Core as needed. There are hooks to
27 let the HCI driver handle proprietary events or override standard behavior.
28 HCI uses 2 execution contexts:
29
30 - one for executing commands : nfc_hci_msg_tx_work(). Only one command
31 can be executing at any given moment.
32 - one for dispatching received events and commands : nfc_hci_msg_rx_work().
33
34 HCI Session initialization
35 --------------------------
36
37 The Session initialization is an HCI standard which must unfortunately
38 support proprietary gates. This is the reason why the driver will pass a list
39 of proprietary gates that must be part of the session. HCI will ensure all
40 those gates have pipes connected when the hci device is set up.
41 In case the chip supports pre-opened gates and pseudo-static pipes, the driver
42 can pass that information to HCI core.
43
44 HCI Gates and Pipes
45 -------------------
46
47 A gate defines the 'port' where some service can be found. In order to access
48 a service, one must create a pipe to that gate and open it. In this
49 implementation, pipes are totally hidden. The public API only knows gates.
50 This is consistent with the driver need to send commands to proprietary gates
51 without knowing the pipe connected to it.
52
53 Driver interface
54 ----------------
55
56 A driver is generally written in two parts : the physical link management and
57 the HCI management. This makes it easier to maintain a driver for a chip that
58 can be connected using various phy (i2c, spi, ...)
59
60 HCI Management
61 --------------
62
63 A driver would normally register itself with HCI and provide the following
64 entry points::
65
66 struct nfc_hci_ops {
67 int (*open)(struct nfc_hci_dev *hdev);
68 void (*close)(struct nfc_hci_dev *hdev);
69 int (*hci_ready) (struct nfc_hci_dev *hdev);
70 int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
71 int (*start_poll) (struct nfc_hci_dev *hdev,
72 u32 im_protocols, u32 tm_protocols);
73 int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target,
74 u8 comm_mode, u8 *gb, size_t gb_len);
75 int (*dep_link_down)(struct nfc_hci_dev *hdev);
76 int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
77 struct nfc_target *target);
78 int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
79 struct nfc_target *target);
80 int (*im_transceive) (struct nfc_hci_dev *hdev,
81 struct nfc_target *target, struct sk_buff *skb,
82 data_exchange_cb_t cb, void *cb_context);
83 int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
84 int (*check_presence)(struct nfc_hci_dev *hdev,
85 struct nfc_target *target);
86 int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
87 struct sk_buff *skb);
88 };
89
90 - open() and close() shall turn the hardware on and off.
91 - hci_ready() is an optional entry point that is called right after the hci
92 session has been set up. The driver can use it to do additional initialization
93 that must be performed using HCI commands.
94 - xmit() shall simply write a frame to the physical link.
95 - start_poll() is an optional entrypoint that shall set the hardware in polling
96 mode. This must be implemented only if the hardware uses proprietary gates or a
97 mechanism slightly different from the HCI standard.
98 - dep_link_up() is called after a p2p target has been detected, to finish
99 the p2p connection setup with hardware parameters that need to be passed back
100 to nfc core.
101 - dep_link_down() is called to bring the p2p link down.
102 - target_from_gate() is an optional entrypoint to return the nfc protocols
103 corresponding to a proprietary gate.
104 - complete_target_discovered() is an optional entry point to let the driver
105 perform additional proprietary processing necessary to auto activate the
106 discovered target.
107 - im_transceive() must be implemented by the driver if proprietary HCI commands
108 are required to send data to the tag. Some tag types will require custom
109 commands, others can be written to using the standard HCI commands. The driver
110 can check the tag type and either do proprietary processing, or return 1 to ask
111 for standard processing. The data exchange command itself must be sent
112 asynchronously.
113 - tm_send() is called to send data in the case of a p2p connection
114 - check_presence() is an optional entry point that will be called regularly
115 by the core to check that an activated tag is still in the field. If this is
116 not implemented, the core will not be able to push tag_lost events to the user
117 space
118 - event_received() is called to handle an event coming from the chip. Driver
119 can handle the event or return 1 to let HCI attempt standard processing.
120
121 On the rx path, the driver is responsible to push incoming HCP frames to HCI
122 using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling
123 This must be done from a context that can sleep.
124
125 PHY Management
126 --------------
127
128 The physical link (i2c, ...) management is defined by the following structure::
129
130 struct nfc_phy_ops {
131 int (*write)(void *dev_id, struct sk_buff *skb);
132 int (*enable)(void *dev_id);
133 void (*disable)(void *dev_id);
134 };
135
136 enable():
137 turn the phy on (power on), make it ready to transfer data
138 disable():
139 turn the phy off
140 write():
141 Send a data frame to the chip. Note that to enable higher
142 layers such as an llc to store the frame for re-emission, this
143 function must not alter the skb. It must also not return a positive
144 result (return 0 for success, negative for failure).
145
146 Data coming from the chip shall be sent directly to nfc_hci_recv_frame().
147
148 LLC
149 ---
150
151 Communication between the CPU and the chip often requires some link layer
152 protocol. Those are isolated as modules managed by the HCI layer. There are
153 currently two modules : nop (raw transfer) and shdlc.
154 A new llc must implement the following functions::
155
156 struct nfc_llc_ops {
157 void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
158 rcv_to_hci_t rcv_to_hci, int tx_headroom,
159 int tx_tailroom, int *rx_headroom, int *rx_tailroom,
160 llc_failure_t llc_failure);
161 void (*deinit) (struct nfc_llc *llc);
162 int (*start) (struct nfc_llc *llc);
163 int (*stop) (struct nfc_llc *llc);
164 void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
165 int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
166 };
167
168 init():
169 allocate and init your private storage
170 deinit():
171 cleanup
172 start():
173 establish the logical connection
174 stop ():
175 terminate the logical connection
176 rcv_from_drv():
177 handle data coming from the chip, going to HCI
178 xmit_from_hci():
179 handle data sent by HCI, going to the chip
180
181 The llc must be registered with nfc before it can be used. Do that by
182 calling::
183
184 nfc_llc_register(const char *name, const struct nfc_llc_ops *ops);
185
186 Again, note that the llc does not handle the physical link. It is thus very
187 easy to mix any physical link with any llc for a given chip driver.
188
189 Included Drivers
190 ----------------
191
192 An HCI based driver for an NXP PN544, connected through I2C bus, and using
193 shdlc is included.
194
195 Execution Contexts
196 ------------------
197
198 The execution contexts are the following:
199 - IRQ handler (IRQH):
200 fast, cannot sleep. sends incoming frames to HCI where they are passed to
201 the current llc. In case of shdlc, the frame is queued in shdlc rx queue.
202
203 - SHDLC State Machine worker (SMW)
204
205 Only when llc_shdlc is used: handles shdlc rx & tx queues.
206
207 Dispatches HCI cmd responses.
208
209 - HCI Tx Cmd worker (MSGTXWQ)
210
211 Serializes execution of HCI commands.
212
213 Completes execution in case of response timeout.
214
215 - HCI Rx worker (MSGRXWQ)
216
217 Dispatches incoming HCI commands or events.
218
219 - Syscall context from a userspace call (SYSCALL)
220
221 Any entrypoint in HCI called from NFC Core
222
223 Workflow executing an HCI command (using shdlc)
224 -----------------------------------------------
225
226 Executing an HCI command can easily be performed synchronously using the
227 following API::
228
229 int nfc_hci_send_cmd (struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
230 const u8 *param, size_t param_len, struct sk_buff **skb)
231
232 The API must be invoked from a context that can sleep. Most of the time, this
233 will be the syscall context. skb will return the result that was received in
234 the response.
235
236 Internally, execution is asynchronous. So all this API does is to enqueue the
237 HCI command, setup a local wait queue on stack, and wait_event() for completion.
238 The wait is not interruptible because it is guaranteed that the command will
239 complete after some short timeout anyway.
240
241 MSGTXWQ context will then be scheduled and invoke nfc_hci_msg_tx_work().
242 This function will dequeue the next pending command and send its HCP fragments
243 to the lower layer which happens to be shdlc. It will then start a timer to be
244 able to complete the command with a timeout error if no response arrive.
245
246 SMW context gets scheduled and invokes nfc_shdlc_sm_work(). This function
247 handles shdlc framing in and out. It uses the driver xmit to send frames and
248 receives incoming frames in an skb queue filled from the driver IRQ handler.
249 SHDLC I(nformation) frames payload are HCP fragments. They are aggregated to
250 form complete HCI frames, which can be a response, command, or event.
251
252 HCI Responses are dispatched immediately from this context to unblock
253 waiting command execution. Response processing involves invoking the completion
254 callback that was provided by nfc_hci_msg_tx_work() when it sent the command.
255 The completion callback will then wake the syscall context.
256
257 It is also possible to execute the command asynchronously using this API::
258
259 static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
260 const u8 *param, size_t param_len,
261 data_exchange_cb_t cb, void *cb_context)
262
263 The workflow is the same, except that the API call returns immediately, and
264 the callback will be called with the result from the SMW context.
265
266 Workflow receiving an HCI event or command
267 ------------------------------------------
268
269 HCI commands or events are not dispatched from SMW context. Instead, they are
270 queued to HCI rx_queue and will be dispatched from HCI rx worker
271 context (MSGRXWQ). This is done this way to allow a cmd or event handler
272 to also execute other commands (for example, handling the
273 NFC_HCI_EVT_TARGET_DISCOVERED event from PN544 requires to issue an
274 ANY_GET_PARAMETER to the reader A gate to get information on the target
275 that was discovered).
276
277 Typically, such an event will be propagated to NFC Core from MSGRXWQ context.
278
279 Error management
280 ----------------
281
282 Errors that occur synchronously with the execution of an NFC Core request are
283 simply returned as the execution result of the request. These are easy.
284
285 Errors that occur asynchronously (e.g. in a background protocol handling thread)
286 must be reported such that upper layers don't stay ignorant that something
287 went wrong below and know that expected events will probably never happen.
288 Handling of these errors is done as follows:
289
290 - driver (pn544) fails to deliver an incoming frame: it stores the error such
291 that any subsequent call to the driver will result in this error. Then it
292 calls the standard nfc_shdlc_recv_frame() with a NULL argument to report the
293 problem above. shdlc stores a EREMOTEIO sticky status, which will trigger
294 SMW to report above in turn.
295
296 - SMW is basically a background thread to handle incoming and outgoing shdlc
297 frames. This thread will also check the shdlc sticky status and report to HCI
298 when it discovers it is not able to run anymore because of an unrecoverable
299 error that happened within shdlc or below. If the problem occurs during shdlc
300 connection, the error is reported through the connect completion.
301
302 - HCI: if an internal HCI error happens (frame is lost), or HCI is reported an
303 error from a lower layer, HCI will either complete the currently executing
304 command with that error, or notify NFC Core directly if no command is
305 executing.
306
307 - NFC Core: when NFC Core is notified of an error from below and polling is
308 active, it will send a tag discovered event with an empty tag list to the user
309 space to let it know that the poll operation will never be able to detect a
310 tag. If polling is not active and the error was sticky, lower levels will
311 return it at next invocation.
312

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

NFC Core용 HCI backend

1-15

Eric Lapuyade와 Samuel Ortiz가 작성한 HCI layer는 ETSI TS 102 622 V10.2.0의 많은 부분을 구현해 HCI 기반 NFC driver 작성을 단순화합니다.

NFC Core backend로서 abstract NFC device를 구현하고 NFC Core API를 HCI command와 event로 변환합니다.

HCI backend 위치
UserspaceNFC Core APIHCI backendHCI command/eventHost controller

Userspace API와 controller protocol 사이를 변환합니다.

========================
HCI backend for NFC Core
========================

- Author: Eric Lapuyade, Samuel Ortiz
- Contact: eric.lapuyade@intel.com, samuel.ortiz@intel.com

General
-------

The HCI layer implements much of the ETSI TS 102 622 V10.2.0 specification. It
enables easy writing of HCI-based NFC drivers. The HCI layer runs as an NFC Core
backend, implementing an abstract nfc device and translating NFC Core API
to HCI commands and events.

Command·event와 실행 context

16-33

HCI는 NFC Core에 NFC device로 등록됩니다. Userspace request는 netlink socket, NFC Core, HCI를 거쳐 host controller에 보낼 HCI command sequence로 변환됩니다.

Command는 sending context가 response를 기다리는 synchronous 방식 또는 HCI Rx context에서 response를 돌려주는 asynchronous 방식으로 실행할 수 있습니다.

Controller에서 받은 HCI event는 처리 후 필요에 따라 NFC Core로 변환해 전달합니다. Driver가 proprietary event를 처리하거나 standard behavior를 override할 hook도 있습니다.

Command 실행은 `nfc_hci_msg_tx_work()` context에서 serialize되어 한 번에 하나만 실행됩니다. 수신 command와 event dispatch는 `nfc_hci_msg_rx_work()` context가 담당합니다.

HCI execution context
Worker역할
`nfc_hci_msg_tx_work()`Command 직렬 실행, 한 번에 하나
`nfc_hci_msg_rx_work()`수신 command와 event dispatch

HCI
---

HCI registers as an nfc device with NFC Core. Requests coming from userspace are
routed through netlink sockets to NFC Core and then to HCI. From this point,
they are translated in a sequence of HCI commands sent to the HCI layer in the
host controller (the chip). Commands can be executed synchronously (the sending
context blocks waiting for response) or asynchronously (the response is returned
from HCI Rx context).
HCI events can also be received from the host controller. They will be handled
and a translation will be forwarded to NFC Core as needed. There are hooks to
let the HCI driver handle proprietary events or override standard behavior.
HCI uses 2 execution contexts:

- one for executing commands : nfc_hci_msg_tx_work(). Only one command
  can be executing at any given moment.
- one for dispatching received events and commands : nfc_hci_msg_rx_work().

Session, gate, pipe

34-52

HCI session initialization은 standard이지만 proprietary gate도 지원해야 합니다. Driver는 session에 포함할 proprietary gate 목록을 전달하고 HCI는 device setup 때 모든 gate에 pipe가 연결되도록 보장합니다.

Chip이 pre-opened gate와 pseudo-static pipe를 지원하면 driver가 이 정보도 HCI core에 전달합니다.

Gate는 service가 있는 port를 정의합니다. Service 접근에는 gate로 pipe를 만들고 열어야 하지만 이 구현은 pipe를 완전히 숨기고 public API에는 gate만 노출합니다. 따라서 driver는 연결된 pipe 번호를 몰라도 proprietary gate에 command를 보낼 수 있습니다.

Gate와 pipe abstraction
DriverGate IDHCI coreHidden pipe create/openController service

Public API는 service gate만 다루고 core가 실제 pipe를 관리합니다.

HCI Session initialization
--------------------------

The Session initialization is an HCI standard which must unfortunately
support proprietary gates. This is the reason why the driver will pass a list
of proprietary gates that must be part of the session. HCI will ensure all
those gates have pipes connected when the hci device is set up.
In case the chip supports pre-opened gates and pseudo-static pipes, the driver
can pass that information to HCI core.

HCI Gates and Pipes
-------------------

A gate defines the 'port' where some service can be found. In order to access
a service, one must create a pipe to that gate and open it. In this
implementation, pipes are totally hidden. The public API only knows gates.
This is consistent with the driver need to send commands to proprietary gates
without knowing the pipe connected to it.

HCI driver management callback

53-124

Driver는 physical link management와 HCI management 두 부분으로 나누는 것이 일반적입니다. 그러면 같은 chip을 I2C, SPI 등 여러 PHY에 연결하는 구현을 쉽게 유지할 수 있습니다.

`nfc_hci_ops.open/close`는 hardware power를 켜고 끕니다. Optional `hci_ready`는 session setup 직후 HCI command를 이용한 추가 초기화를 수행합니다. `xmit`은 physical link에 frame을 씁니다.

Optional `start_poll`은 proprietary gate나 standard와 다른 mechanism을 쓰는 hardware의 polling mode를 설정합니다. `dep_link_up/down`은 P2P target 연결을 완성하거나 내립니다.

`target_from_gate`는 proprietary gate의 NFC protocol을 반환하고 `complete_target_discovered`는 target auto-activation에 필요한 추가 proprietary 처리를 수행합니다.

`im_transceive`는 tag data에 proprietary HCI command가 필요할 때 구현합니다. Tag type을 확인해 직접 처리하거나 1을 반환해 standard 처리로 넘깁니다. Data exchange command는 asynchronous여야 합니다.

`tm_send`는 P2P data 전송, `check_presence`는 활성 tag가 field에 남아 있는지 주기적으로 확인합니다. 후자가 없으면 core가 userspace에 `tag_lost`를 보낼 수 없습니다.

`event_received`는 chip event를 처리하거나 1을 반환해 standard HCI 처리로 넘깁니다. Rx path의 driver는 incoming HCP frame을 sleep 가능한 context에서 `nfc_hci_recv_frame()`에 전달하고 HCI가 re-aggregation과 처리를 담당합니다.

`nfc_hci_ops`
Callback역할
`open/close`Hardware power
`hci_ready`Session 후 optional init
`xmit`PHY frame write
`start_poll`Proprietary polling
`dep_link_up/down`P2P link
`target_from_gate`Gate에서 protocol 결정
`complete_target_discovered`Target activation 보완
`im_transceive`Initiator-mode tag data
`tm_send`Target-mode/P2P data
`check_presence`Tag field presence
`event_received`Proprietary event

Driver interface
----------------

A driver is generally written in two parts : the physical link management and
the HCI management. This makes it easier to maintain a driver for a chip that
can be connected using various phy (i2c, spi, ...)

HCI Management
--------------

A driver would normally register itself with HCI and provide the following
entry points::

  struct nfc_hci_ops {
        int (*open)(struct nfc_hci_dev *hdev);
        void (*close)(struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
        int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*start_poll) (struct nfc_hci_dev *hdev,
                           u32 im_protocols, u32 tm_protocols);
        int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target,
                           u8 comm_mode, u8 *gb, size_t gb_len);
        int (*dep_link_down)(struct nfc_hci_dev *hdev);
        int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
                                 struct nfc_target *target);
        int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
                                           struct nfc_target *target);
        int (*im_transceive) (struct nfc_hci_dev *hdev,
                              struct nfc_target *target, struct sk_buff *skb,
                              data_exchange_cb_t cb, void *cb_context);
        int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
        int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                              struct sk_buff *skb);
  };

- open() and close() shall turn the hardware on and off.
- hci_ready() is an optional entry point that is called right after the hci
  session has been set up. The driver can use it to do additional initialization
  that must be performed using HCI commands.
- xmit() shall simply write a frame to the physical link.
- start_poll() is an optional entrypoint that shall set the hardware in polling
  mode. This must be implemented only if the hardware uses proprietary gates or a
  mechanism slightly different from the HCI standard.
- dep_link_up() is called after a p2p target has been detected, to finish
  the p2p connection setup with hardware parameters that need to be passed back
  to nfc core.
- dep_link_down() is called to bring the p2p link down.
- target_from_gate() is an optional entrypoint to return the nfc protocols
  corresponding to a proprietary gate.
- complete_target_discovered() is an optional entry point to let the driver
  perform additional proprietary processing necessary to auto activate the
  discovered target.
- im_transceive() must be implemented by the driver if proprietary HCI commands
  are required to send data to the tag. Some tag types will require custom
  commands, others can be written to using the standard HCI commands. The driver
  can check the tag type and either do proprietary processing, or return 1 to ask
  for standard processing. The data exchange command itself must be sent
  asynchronously.
- tm_send() is called to send data in the case of a p2p connection
- check_presence() is an optional entry point that will be called regularly
  by the core to check that an activated tag is still in the field. If this is
  not implemented, the core will not be able to push tag_lost events to the user
  space
- event_received() is called to handle an event coming from the chip. Driver
  can handle the event or return 1 to let HCI attempt standard processing.

On the rx path, the driver is responsible to push incoming HCP frames to HCI
using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling
This must be done from a context that can sleep.

PHY management

125-147

I2C 같은 physical link는 `nfc_phy_ops`의 `write`, `enable`, `disable`로 정의합니다.

`enable`은 PHY power를 켜고 data transfer 준비를 하며 `disable`은 끕니다. `write`는 chip에 data frame을 보냅니다.

LLC 등 상위 layer가 frame을 retransmission용으로 보관할 수 있도록 `write`는 `skb`를 변경하면 안 됩니다. 성공은 0, 실패는 음수를 반환하며 양수는 반환하지 않습니다.

Chip에서 온 data는 `nfc_hci_recv_frame()`으로 직접 보냅니다.

`nfc_phy_ops`
Callback계약
`enable`Power on, transfer ready
`disable`Power off
`write``skb` 불변, 0 또는 음수 반환

PHY Management
--------------

The physical link (i2c, ...) management is defined by the following structure::

  struct nfc_phy_ops {
        int (*write)(void *dev_id, struct sk_buff *skb);
        int (*enable)(void *dev_id);
        void (*disable)(void *dev_id);
  };

enable():
        turn the phy on (power on), make it ready to transfer data
disable():
        turn the phy off
write():
        Send a data frame to the chip. Note that to enable higher
        layers such as an llc to store the frame for re-emission, this
        function must not alter the skb. It must also not return a positive
        result (return 0 for success, negative for failure).

Data coming from the chip shall be sent directly to nfc_hci_recv_frame().

LLC module과 포함 driver

148-194

CPU와 chip 통신에는 link-layer protocol이 필요한 경우가 많으며 HCI layer가 module로 분리해 관리합니다. 현재 `nop` raw transfer와 `shdlc` 두 module이 있습니다.

새 LLC는 private storage를 준비하는 `init`, 정리하는 `deinit`, logical connection을 여닫는 `start/stop`, driver에서 HCI 방향의 `rcv_from_drv`, HCI에서 chip 방향의 `xmit_from_hci`를 구현합니다.

사용 전에 `nfc_llc_register(name, ops)`로 NFC에 등록해야 합니다. LLC는 physical link를 처리하지 않으므로 같은 chip driver에서 어떤 PHY와도 조합하기 쉽습니다.

포함된 예시는 I2C에 연결되고 SHDLC를 사용하는 NXP PN544 HCI driver입니다.

LLC와 PHY 분리
HCI`nfc_llc_ops` nop/SHDLC`nfc_phy_ops` I2C/SPINFC chip
`nfc_llc_register()`LLC 선택

Protocol framing과 physical transport를 독립 조합합니다.

LLC
---

Communication between the CPU and the chip often requires some link layer
protocol. Those are isolated as modules managed by the HCI layer. There are
currently two modules : nop (raw transfer) and shdlc.
A new llc must implement the following functions::

  struct nfc_llc_ops {
        void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
                       rcv_to_hci_t rcv_to_hci, int tx_headroom,
                       int tx_tailroom, int *rx_headroom, int *rx_tailroom,
                       llc_failure_t llc_failure);
        void (*deinit) (struct nfc_llc *llc);
        int (*start) (struct nfc_llc *llc);
        int (*stop) (struct nfc_llc *llc);
        void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
        int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
  };

init():
        allocate and init your private storage
deinit():
        cleanup
start():
        establish the logical connection
stop ():
        terminate the logical connection
rcv_from_drv():
        handle data coming from the chip, going to HCI
xmit_from_hci():
        handle data sent by HCI, going to the chip

The llc must be registered with nfc before it can be used. Do that by
calling::

        nfc_llc_register(const char *name, const struct nfc_llc_ops *ops);

Again, note that the llc does not handle the physical link. It is thus very
easy to mix any physical link with any llc for a given chip driver.

Included Drivers
----------------

An HCI based driver for an NXP PN544, connected through I2C bus, and using
shdlc is included.

IRQ·SHDLC·HCI worker context

195-222

IRQ handler, IRQH는 빠르고 sleep할 수 없습니다. Incoming frame을 HCI의 현재 LLC로 보내며 SHDLC에서는 Rx queue에 넣습니다.

SHDLC State Machine worker, SMW는 SHDLC 사용 시 Rx/Tx queue와 HCI command response dispatch를 처리합니다.

HCI Tx worker, MSGTXWQ는 command 실행을 serialize하고 response timeout이면 완료 처리합니다. HCI Rx worker, MSGRXWQ는 incoming HCI command와 event를 dispatch합니다.

SYSCALL은 userspace call에서 NFC Core를 거쳐 HCI entry point를 호출하는 context입니다.

실행 context
ContextSleep역할
IRQH불가Frame을 LLC queue로 전달
SMWWorkerSHDLC Rx/Tx와 response
MSGTXWQWorkerHCI command 직렬화·timeout
MSGRXWQWorkerHCI command/event dispatch
SYSCALL가능NFC Core에서 HCI API 호출

Execution Contexts
------------------

The execution contexts are the following:
- IRQ handler (IRQH):
fast, cannot sleep. sends incoming frames to HCI where they are passed to
the current llc. In case of shdlc, the frame is queued in shdlc rx queue.

- SHDLC State Machine worker (SMW)

  Only when llc_shdlc is used: handles shdlc rx & tx queues.

  Dispatches HCI cmd responses.

- HCI Tx Cmd worker (MSGTXWQ)

  Serializes execution of HCI commands.

  Completes execution in case of response timeout.

- HCI Rx worker (MSGRXWQ)

  Dispatches incoming HCI commands or events.

- Syscall context from a userspace call (SYSCALL)

  Any entrypoint in HCI called from NFC Core

HCI command 실행 흐름

223-265

`nfc_hci_send_cmd()`는 sleep 가능한 context, 대개 syscall에서 synchronous API로 호출하며 response `skb`를 돌려줍니다.

내부 실행은 asynchronous입니다. API는 command를 enqueue하고 stack의 local wait queue를 설정한 뒤 `wait_event()`로 완료를 기다립니다. 짧은 timeout 안에 반드시 끝나므로 wait는 interruptible이 아닙니다.

MSGTXWQ의 `nfc_hci_msg_tx_work()`가 command를 dequeue하고 HCP fragment를 SHDLC에 보내며 response timeout timer를 시작합니다.

SMW의 `nfc_shdlc_sm_work()`가 framing과 Rx/Tx queue를 처리합니다. SHDLC I-frame payload인 HCP fragment를 response·command·event의 완전한 HCI frame으로 aggregate합니다.

Response는 SMW에서 즉시 completion callback으로 dispatch되어 대기 중인 syscall을 깨웁니다.

`nfc_hci_execute_cmd_async()`는 같은 흐름을 사용하지만 즉시 반환하고 SMW context가 결과 callback을 호출합니다.

HCI command workflow
SYSCALL `nfc_hci_send_cmd()`Queue + wait
MSGTXWQHCP fragment Tx + timer
SMW/SHDLCFrame I/O·aggregation
Response callbackSYSCALL wake
Async API즉시 returnSMW callback

Synchronous API 뒤에서 worker와 SHDLC가 비동기로 실행됩니다.

Workflow executing an HCI command (using shdlc)
-----------------------------------------------

Executing an HCI command can easily be performed synchronously using the
following API::

  int nfc_hci_send_cmd (struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
                        const u8 *param, size_t param_len, struct sk_buff **skb)

The API must be invoked from a context that can sleep. Most of the time, this
will be the syscall context. skb will return the result that was received in
the response.

Internally, execution is asynchronous. So all this API does is to enqueue the
HCI command, setup a local wait queue on stack, and wait_event() for completion.
The wait is not interruptible because it is guaranteed that the command will
complete after some short timeout anyway.

MSGTXWQ context will then be scheduled and invoke nfc_hci_msg_tx_work().
This function will dequeue the next pending command and send its HCP fragments
to the lower layer which happens to be shdlc. It will then start a timer to be
able to complete the command with a timeout error if no response arrive.

SMW context gets scheduled and invokes nfc_shdlc_sm_work(). This function
handles shdlc framing in and out. It uses the driver xmit to send frames and
receives incoming frames in an skb queue filled from the driver IRQ handler.
SHDLC I(nformation) frames payload are HCP fragments. They are aggregated to
form complete HCI frames, which can be a response, command, or event.

HCI Responses are dispatched immediately from this context to unblock
waiting command execution. Response processing involves invoking the completion
callback that was provided by nfc_hci_msg_tx_work() when it sent the command.
The completion callback will then wake the syscall context.

It is also possible to execute the command asynchronously using this API::

  static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
                                       const u8 *param, size_t param_len,
                                       data_exchange_cb_t cb, void *cb_context)

The workflow is the same, except that the API call returns immediately, and
the callback will be called with the result from the SMW context.

수신 event·command workflow

266-278

Incoming HCI command나 event는 SMW에서 직접 dispatch하지 않고 HCI `rx_queue`에 넣어 MSGRXWQ가 처리합니다.

이 분리는 event handler가 다른 HCI command도 실행할 수 있게 합니다. 예를 들어 PN544의 `NFC_HCI_EVT_TARGET_DISCOVERED` 처리는 reader A gate에 `ANY_GET_PARAMETER`를 보내 target 정보를 가져와야 합니다.

일반적으로 event는 MSGRXWQ context에서 NFC Core로 전파됩니다.

Incoming event
SMWHCI `rx_queue`MSGRXWQEvent handlerOptional nested HCI commandNFC Core

SHDLC worker와 event handler context를 분리해 handler의 command 실행을 허용합니다.

Workflow receiving an HCI event or command
------------------------------------------

HCI commands or events are not dispatched from SMW context. Instead, they are
queued to HCI rx_queue and will be dispatched from HCI rx worker
context (MSGRXWQ). This is done this way to allow a cmd or event handler
to also execute other commands (for example, handling the
NFC_HCI_EVT_TARGET_DISCOVERED event from PN544 requires to issue an
ANY_GET_PARAMETER to the reader A gate to get information on the target
that was discovered).

Typically, such an event will be propagated to NFC Core from MSGRXWQ context.

동기·비동기 error 전파

279-311

NFC Core request 실행과 동시에 발생한 error는 request result로 반환합니다. Background protocol thread의 asynchronous error는 upper layer가 예정된 event가 오지 않을 사실을 알 수 있도록 명시적으로 전파해야 합니다.

PN544 driver가 incoming frame 전달에 실패하면 error를 저장해 이후 call이 같은 error를 반환하게 하고 `nfc_shdlc_recv_frame(NULL)`로 위에 알립니다. SHDLC는 `EREMOTEIO` sticky status를 저장합니다.

SMW는 sticky status를 확인해 unrecoverable error로 더 실행할 수 없으면 HCI에 보고합니다. SHDLC connection 중이면 connect completion으로 전달합니다.

HCI internal frame loss나 lower-layer error는 현재 command를 해당 error로 완료하거나, 실행 중 command가 없으면 NFC Core에 직접 통지합니다.

Polling 중 NFC Core가 error를 받으면 빈 tag list의 discovered event를 userspace에 보내 더 이상 tag를 찾을 수 없음을 알립니다. Polling이 아니고 sticky error라면 다음 lower-layer call에서 반환됩니다.

Asynchronous error propagation
PN544 frame failure`nfc_shdlc_recv_frame(NULL)`
SHDLC `EREMOTEIO` stickySMW
HCI current command error 또는 direct notifyNFC Core
PollingEmpty tag list event
다음 callSticky error return

Driver에서 sticky 상태를 거쳐 HCI와 NFC Core, userspace까지 손실 없이 전파합니다.

Error management
----------------

Errors that occur synchronously with the execution of an NFC Core request are
simply returned as the execution result of the request. These are easy.

Errors that occur asynchronously (e.g. in a background protocol handling thread)
must be reported such that upper layers don't stay ignorant that something
went wrong below and know that expected events will probably never happen.
Handling of these errors is done as follows:

- driver (pn544) fails to deliver an incoming frame: it stores the error such
  that any subsequent call to the driver will result in this error. Then it
  calls the standard nfc_shdlc_recv_frame() with a NULL argument to report the
  problem above. shdlc stores a EREMOTEIO sticky status, which will trigger
  SMW to report above in turn.

- SMW is basically a background thread to handle incoming and outgoing shdlc
  frames. This thread will also check the shdlc sticky status and report to HCI
  when it discovers it is not able to run anymore because of an unrecoverable
  error that happened within shdlc or below. If the problem occurs during shdlc
  connection, the error is reported through the connect completion.

- HCI: if an internal HCI error happens (frame is lost), or HCI is reported an
  error from a lower layer, HCI will either complete the currently executing
  command with that error, or notify NFC Core directly if no command is
  executing.

- NFC Core: when NFC Core is notified of an error from below and polling is
  active, it will send a tag discovered event with an empty tag list to the user
  space to let it know that the poll operation will never be able to detect a
  tag. If polling is not active and the error was sticky, lower levels will
  return it at next invocation.