Before you start#
Review network devices and NAPI, device lifetimes, and RCU. All source claims use upstream v6.12. Wi-Fi or Bluetooth hardware, regulatory settings and firmware are not guaranteed in a QEMU guest; the exercise uses synthetic device state and never controls a radio.
Learning goals#
Separate a wireless radio from a network interface, explain registration and teardown of a wiphy, distinguish cfg80211 from mac80211 and Bluetooth, and reason about regulatory and rfkill boundaries.
Source walkthrough#
- Feature bit
Driver advertises TDLS channel switching
- Callbacks
Both switch and cancel operations must exist
- Registration check
Missing operation returns EINVAL
- Removal
List deletion precedes an RCU grace period
A wiphy is cfg80211's representation of a physical wireless device; it can have zero, one, or many virtual interfaces. Each interface has wireless-specific state described by wireless_dev, commonly associated with a network device. Supported interface combinations and concurrency are constrained by the device, firmware, driver, and applicable regulatory rules. A visible interface therefore does not imply every advertised mode can run simultaneously. cfg80211 device registration
cfg80211 implements configuration interfaces and coordinates operations with drivers. nl80211 is the netlink userspace interface for many wireless operations. mac80211 supplies a shared 802.11 implementation for drivers using its model; a full-MAC device can implement more protocol functions in firmware instead. The names do not imply that all wireless hardware uses mac80211, or that firmware obeys identical timing on every device.
In net/wireless/core.c, wiphy_register validates advertised capabilities against supplied operations. This v6.12 GPL-2.0-only excerpt omits surrounding checks:
if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
(!rdev->ops->tdls_channel_switch ||
!rdev->ops->tdls_cancel_channel_switch)))
return -EINVAL;
The first line tests the advertised TDLS channel-switch feature. The second and third test whether either required operation is absent. If the feature is claimed without both callbacks, the condition warns and registration returns -EINVAL. A feature bit is therefore a contract with implementation requirements, not just a label userspace can trust independently. Other checks in this function cover different capability combinations; this example does not imply these are the only required callbacks.
Registration, removal, and concurrent readers#
Imagine driver A supplies both callbacks and sets the feature bit. This one check passes, but subsequent registration checks may still fail; success cannot be inferred from it alone. Driver B sets the bit with a missing cancellation callback: this check fails before successful registration. Driver C does not set the feature: this particular condition is false regardless of those two callback pointers, though other checks still apply. This is a source-level truth table, not a test of actual devices.
wiphy_unregister illustrates the opposite boundary. It waits for opencount to reach zero, sampling that count under the wiphy lock. It unregisters rfkill when present, acquires RTNL and the wiphy lock, notifies deletion, marks registered false, removes the device from the RCU-protected list and calls synchronize_rcu. The grace period ensures earlier RCU readers can finish before later teardown proceeds; list removal alone would not guarantee that. The source warns if the wireless-device list is nonempty. Neither a lock nor an RCU grace period is a substitute for all required object references and driver-specific teardown.
In net/wireless/core.c, v6.12, GPL-2.0-only (the function from its opening through the RCU grace period; the later regulatory and driver cleanup is omitted):
void wiphy_unregister(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
wait_event(rdev->dev_wait, ({
int __count;
wiphy_lock(&rdev->wiphy);
__count = rdev->opencount;
wiphy_unlock(&rdev->wiphy);
__count == 0; }));
if (rdev->wiphy.rfkill)
rfkill_unregister(rdev->wiphy.rfkill);
rtnl_lock();
wiphy_lock(&rdev->wiphy);
nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
rdev->wiphy.registered = false;
WARN_ON(!list_empty(&rdev->wiphy.wdev_list));
/*
* First remove the hardware from everywhere, this makes
* it impossible to find from userspace.
*/
debugfs_remove_recursive(rdev->wiphy.debugfsdir);
list_del_rcu(&rdev->list);
synchronize_rcu();
The wait_event samples opencount under the wiphy lock and blocks until it reaches zero. The RCU removal is list_del_rcu followed by synchronize_rcu(), and the list check is a warning rather than an error return. Nothing here shows the regulatory or driver teardown that follows, so it does not establish that all device work has finished.
- Wait for opencount
Block until open users drop to zero; sampled under the wiphy lock
- Remove rfkill
rfkill_unregister runs when the wiphy has an rfkill object
- Lock and notify
Take RTNL and the wiphy lock; send the deletion notice; registered = false
- Hide from lookups
Remove debugfs entries and list_del_rcu the device
- Wait for readers
synchronize_rcu lets earlier RCU readers finish
- Regulatory cleanup
wiphy_regulatory_deregister runs after the grace period
The regulatory subsystem constrains which frequencies and operations are permitted under applicable rules and device capabilities. A device's capability declaration is not an authorization to ignore regulatory restrictions. rfkill supplies radio-blocking controls, including hardware and software state where supported; blocking is not equivalent to disconnecting a network namespace, removing every network route, or proving all radios in a machine are disabled. Bluetooth has a separate subsystem, controller interfaces, pairing and link-security behavior; Wi-Fi and Bluetooth can coexist in one package without sharing the same software protocol state. Inspect mac80211's entry code and Bluetooth core for their separate implementations.
From nl80211 request to radio operation#
A userspace network manager communicates wireless configuration through generic netlink and the nl80211 family. A netlink message contains a command and typed attributes, such as the target wireless interface and requested parameters. The cfg80211 core checks the request and dispatches supported operations through the registered driver's cfg80211_ops. The driver may ask firmware to perform work, or may use mac80211 and its hardware driver to implement portions of the 802.11 protocol. This is a boundary diagram, not one universal call chain: operation, interface type, hardware model, firmware and configuration determine the concrete callbacks.
A scan seeks information about nearby networks on allowed channels; a connect operation attempts to establish a station connection to a selected network. Authentication and association are distinct 802.11 steps. Authentication establishes a protocol relationship appropriate to the selected method; association establishes participation in a particular basic service set. A successful association does not itself prove that higher-layer IP configuration, name resolution or Internet connectivity works. Security key negotiation and userspace involvement vary by authentication method and implementation. A network manager, supplicant, kernel and firmware can divide these tasks differently; record their versions and logs before attributing a failure to a single layer.
The following v6.12 GPL-2.0-only excerpt is from nl80211_trigger_scan in net/wireless/nl80211.c; earlier initialization and later validation are omitted:
if (!rdev->ops->scan)
return -EOPNOTSUPP;
if (rdev->scan_req || rdev->scan_msg)
return -EBUSY;
The first check rejects a scan when the registered operation table lacks the scan callback. The second rejects a new scan while the registered device has a scan request or scan message in progress. These are not equivalent errors: the first concerns support for that operation, while the second indicates a conflicting outstanding scan state. The surrounding function checks interface type, channel attributes, requested SSID count and other conditions; a supported, idle scan can still fail for those reasons. The scan request's eventual completion or abortion must be reported through the relevant cfg80211 contract; returning from an asynchronous initiation is not the same as finding an access point.
- Network managernl80211_trigger_scan
Scan request (netlink attributes)
- nl80211_trigger_scanNetwork manager
Return. -EOPNOTSUPP if rdev->ops->scan is absent
- nl80211_trigger_scancfg80211 device state
Look up scan_req and scan_msg
- cfg80211 device stateNetwork manager
Return. -EBUSY while a scan is outstanding
- nl80211_trigger_scanNetwork manager
Return. Remaining validation can still reject the request
Time runs downward. Dashed arrows show returns.
Read the text version
- Network manager → nl80211_trigger_scan: Scan request (netlink attributes)
- nl80211_trigger_scan → Network manager (return): -EOPNOTSUPP if rdev->ops->scan is absent
- nl80211_trigger_scan → cfg80211 device state: Look up scan_req and scan_msg
- cfg80211 device state → Network manager (return): -EBUSY while a scan is outstanding
- nl80211_trigger_scan → Network manager (return): Remaining validation can still reject the request
Imagine a station interface requests a scan while scan_req is non-null. The source returns -EBUSY before starting another request through this path. After the existing operation has completed and the driver/core have cleared its state through the appropriate completion path, another request may be attempted; it can still fail for a different reason. If the callback is absent, waiting for the current request to complete cannot create the missing operation. That distinction directs debugging: capture the error, target interface and concurrent users before changing channel or regulatory settings. The lab below deliberately does not issue a scan.
Soft-MAC, full-MAC, and asynchronous state#
A soft-MAC design places significant 802.11 management in the host stack, commonly using mac80211. A full-MAC design places more management in device firmware and exposes operations to cfg80211 through its driver. These are useful categories rather than a guarantee that every device implements exactly the same split. For a soft-MAC path, mac80211 manages software state and invokes hardware-driver operations appropriate to the device; for a full-MAC path, firmware may handle scan or association and report results. The mac80211 entry code and cfg80211 documentation are starting points for selecting an actual driver and its callbacks.
Network operations are asynchronous. A request can schedule work, receive firmware events or complete from another context; state and object references must remain valid until completion or cancellation. Workqueues execute deferred work in process context, but a work item is not automatically safe after its device is unregistered. A removal path has to prevent new work and synchronize or cancel outstanding operations according to the concrete driver's contract. The wiphy unregister example waits for open users, removes the object from the discoverable list and waits for RCU readers. That is one part of teardown, not proof that every driver-specific work item has already finished without additional cleanup.
A wiphy represents radio-level capabilities, while a wireless_dev and a network device represent interfaces using those capabilities. One physical radio may expose a station interface and other supported interface types, subject to valid combinations. Deleting a netdev does not necessarily mean the whole radio object has been freed; removing the radio requires its interface and driver lifecycle to be resolved. During an unregister race, a reader that found an object before list removal can still hold an RCU read-side view. Removal followed by the grace period prevents freeing storage before those earlier readers have passed their protected section, while references and locks address additional lifetime and mutation requirements.
Bluetooth is a separate protocol path#
Bluetooth has its own controller and host protocols. HCI, the Host Controller Interface, carries commands, events and data between the host stack and controller over a transport such as USB, UART or another supported bus. A controller reports connection events, and Linux associates a connection object with later data. L2CAP, the Logical Link Control and Adaptation Protocol, handles logical channels, multiplexing and reassembly above HCI ACL data. Bluetooth Classic and Low Energy have different procedures and capabilities; the presence of an HCI device does not prove a peer is paired, encrypted, connected or usable.
In net/bluetooth/l2cap_core.c, l2cap_recv_acldata receives an HCI connection, a socket buffer and packet flags. The following exact v6.12 excerpt appears in that function; surrounding statements are omitted and the source's license and copyright notice apply:
if (!conn)
conn = l2cap_conn_add(hcon);
if (!conn)
goto drop;
The first condition attempts to add an L2CAP connection when the HCI connection has no existing one. The second handles failure by taking the drop path rather than dereferencing a missing connection. The subsequent switch handles start and continuation flags, frame length and fragmentation. For a complete start frame, it can call l2cap_recv_frame; for a fragment, it must accumulate enough data before interpreting the completed L2CAP frame. Unexpected starts during an existing reassembly trigger reset and an unreliable-connection indication in this source path. A sk_buff is a kernel packet buffer whose ownership follows these branches; a reader should trace each return or drop before claiming where it is freed.
As a concrete counterexample, suppose an ACL start arrives while conn->rx_skb already holds a partial frame. The source logs an unexpected start, resets the existing reassembly and marks the connection unreliable; it does not safely concatenate the new start onto an unrelated old fragment. If no L2CAP connection can be obtained, the excerpt takes the drop path. These are source-level outcomes, not proof that a particular controller emitted either event. HCI connection establishment itself is handled elsewhere, including hci_event.c; do not collapse controller events, packet reassembly and application socket handling into one operation.
Radio policy and firmware#
rfkill exposes blocking state for supported radio transmitters. The kernel documentation defines both hard and soft block as transmitter-off states; hard block is read-only from software, while soft block is software-controlled. Clearing a software block therefore cannot override a hardware block. rfkill is not a general network isolation policy: it does not remove routes or isolate unrelated interfaces. A missing rfkill entry does not prove a radio is active or that every device supports the interface. Firmware, driver restrictions, regulatory constraints, namespace visibility, and permissions can each affect what is observable. Record evidence before selecting an explanation. rfkill documentation
Read-only Linux observation lab#
On your own authorized host or a disposable guest, inspect existing state without scanning, connecting, changing rfkill, or modifying interfaces:
uname -r
if command -v iw >/dev/null 2>&1; then iw dev; iw phy; else echo 'iw unavailable'; fi
if command -v rfkill >/dev/null 2>&1; then rfkill list; else echo 'rfkill unavailable'; fi
ls -l /sys/class/bluetooth 2>/dev/null || echo 'Bluetooth class unavailable'
for p in /sys/class/bluetooth/hci*/address; do [ -r "$p" ] && printf '%s: %s\n' "$p" "$(cat "$p")"; done
iw dev lists visible wireless interfaces, while iw phy reports radio capabilities and restrictions exposed by the running system. rfkill list may report software and hardware block state. Bluetooth class entries identify registered controllers; an address is an identifier, so avoid publishing it unnecessarily. None of these outputs proves an active connection, successful association, scan result, or valid Bluetooth data exchange. Some commands or sysfs attributes may be restricted; missing tools, no radios, an empty class or permission denial should be recorded without escalating privileges. Commands query existing state only; opening or querying a device can still wake some hardware.
Record uname -r, iw --version and rfkill --version where available. iw and the rfkill utility are separate userspace projects; output and permissions depend on their installed versions and the actual running kernel, which may contain distribution backports. Source-only alternative: record the two nl80211_trigger_scan results given above—missing callback yields -EOPNOTSUPP, existing scan_req or scan_msg yields -EBUSY after the callback check—and explain why neither result establishes what an access point transmitted. Add the L2CAP missing-connection drop path to your evidence table. Do not perform an active scan merely to fill in an unavailable observation.
Practice#
Run this unprivileged truth table for the exact boolean check above. It does not call cfg80211 or alter any radio:
python3 - <<'PY'
for advertised in (False, True):
for switch in (False, True):
for cancel in (False, True):
rejected = advertised and (not switch or not cancel)
print(f'feature={advertised} switch={switch} cancel={cancel} reject={rejected}')
PY
Three of the four advertised-feature combinations are rejected; none of the non-advertised combinations is rejected by this particular check. As a counterexample to “feature bit proves support,” use True, True, False: missing cancellation causes rejection. Deliver the eight-row table and a teardown timeline with removal from the list before the RCU grace period. Note that the model excludes all other registration checks and has no kernel interaction.
Knowledge check#
- Does passing this one capability check prove registration succeeded?
- Why does removal from an RCU list precede a grace period?
- Is rfkill equivalent to a complete network isolation policy?
- Why do an absent scan callback and an outstanding scan request require different diagnoses?
- What does L2CAP do when a new ACL start arrives during an unfinished reassembly in the cited path?
Answers#
- No. The function performs further checks and registration steps; any of them can fail. This check only establishes that this advertised feature has both required callbacks.
- Removal prevents new list traversals from finding the object, while the grace period lets readers that were already traversing finish before the relevant later teardown. Separate references and locks can still be required.
- No. rfkill controls supported radio-blocking state. Network isolation, other devices and policy require separate consideration.
- The source returns
-EOPNOTSUPPfor the absent callback, while an outstanding request or scan message returns-EBUSY. Waiting can resolve a temporary conflict but cannot add a missing driver operation. - It resets the partial receive state and marks the connection unreliable rather than combining unrelated fragments. The exact effect on an application depends on the subsequent connection and channel handling.
Checkpoint#
Submit the truth table and a wiphy removal timeline naming the lock-protected open count and RCU grace period. Ready means the missing-callback case rejects and you distinguish radio capability, interface and regulatory permission. Retry by evaluating the excerpt one boolean term at a time. This is a readiness check, not certification.