Skip to content

Commit 45798f6

Browse files
authored
Add Thinkpad NXP1001 NFC support using libnfc-nci and PC/SC (#379118)
2 parents 5156904 + a051988 commit 45798f6

File tree

8 files changed

+375
-15
lines changed

8 files changed

+375
-15
lines changed

nixos/doc/manual/release-notes/rl-2505.section.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@
113113

114114
- [waagent](https://github.com/Azure/WALinuxAgent), the Microsoft Azure Linux Agent (waagent) manages Linux provisioning and VM interaction with the Azure Fabric Controller. Available with [services.waagent](options.html#opt-services.waagent.enable).
115115

116+
- [nfc-nci](https://github.com/StarGate01/ifdnfc-nci), an alternative NFC stack and PC/SC driver for the NXP PN54x chipset, commonly found in Lenovo systems as NXP1001 (NPC300). Available as [hardware.nfc-nci](#opt-hardware.nfc-nci.enable).
117+
116118
- [duckdns](https://www.duckdns.org), free dynamic DNS. Available with [services.duckdns](options.html#opt-services.duckdns.enable)
117119

118120
- [nostr-rs-relay](https://git.sr.ht/~gheartsfield/nostr-rs-relay/), This is a nostr relay, written in Rust. Available as [services.nostr-rs-relay](options.html#opt-services.nostr-rs-relay.enable).

nixos/modules/hardware/nfc-nci.nix

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
8+
let
9+
cfg = config.hardware.nfc-nci;
10+
11+
# To understand these settings in more detail, refer to the upstream configuration templates
12+
# available at https://github.com/NXPNFCLinux/linux_libnfc-nci/tree/master/conf .
13+
# Settings in curly braces are NCI commands, the "NFC Controller Interface Specification"
14+
# as well as the "NFC Digital Protocol Technical Specification" can be found online.
15+
# These default settings have been specifically engineered for the Lenovo NXP1001 (NPC300) chipset.
16+
defaultSettings = {
17+
# This block will be emitted into /etc/libnfc-nci.conf
18+
nci = {
19+
# Set up general logging
20+
APPL_TRACE_LEVEL = "0x01";
21+
PROTOCOL_TRACE_LEVEL = "0x01";
22+
# Set up which NFC technologies are enabled (due to e.g. local regulation or patent law)
23+
HOST_LISTEN_TECH_MASK = "0x07";
24+
POLLING_TECH_MASK = "0xEF";
25+
P2P_LISTEN_TECH_MASK = "0xC5";
26+
};
27+
# This block will be emitted into /etc/libnfc-nxp-init.conf
28+
init = {
29+
# Setup logging of the individual userland library components
30+
NXPLOG_GLOBAL_LOGLEVEL = "0x01";
31+
NXPLOG_EXTNS_LOGLEVEL = "0x01";
32+
NXPLOG_NCIHAL_LOGLEVEL = "0x01";
33+
NXPLOG_NCIX_LOGLEVEL = "0x01";
34+
NXPLOG_NCIR_LOGLEVEL = "0x01";
35+
NXPLOG_FWDNLD_LOGLEVEL = "0x00";
36+
NXPLOG_TML_LOGLEVEL = "0x01";
37+
# Where to find the kernel device node
38+
NXP_NFC_DEV_NODE = ''"/dev/pn544"'';
39+
# Enable the NXP proprietary features of the chip
40+
NXP_ACT_PROP_EXTN = "{2F, 02, 00}";
41+
# Configure the NFC Forum profile:
42+
# 0xA0 0x44: POLL_PROFILE_SEL_CFG = 0x00 (Use NFC Forum profile default configuration values. Specifically, not EMVCo.)
43+
NXP_NFC_PROFILE_EXTN = ''
44+
{20, 02, 05, 01,
45+
A0, 44, 01, 00
46+
}
47+
'';
48+
# Enable chip standby mode
49+
NXP_CORE_STANDBY = "{2F, 00, 01, 01}";
50+
# Enable NCI packet fragmentation on the I2C bus
51+
NXP_I2C_FRAGMENTATION_ENABLED = "0x01";
52+
};
53+
# This block will be emitted into /etc/libnfc-nxp-pn547.conf as well as /etc/libnfc-nxp-pn548.conf
54+
# Which file is actually used is decided by the library at runtime depending on chip variant, both files are required.
55+
pn54x = {
56+
# Enable Mifare Classic reader functionality
57+
MIFARE_READER_ENABLE = "0x01";
58+
# Configure clock source - use XTAL (hardware crystal) instead of PLL (synthetic clock)
59+
NXP_SYS_CLK_SRC_SEL = "0x01";
60+
NXP_SYS_CLK_FREQ_SEL = "0x00";
61+
NXP_SYS_CLOCK_TO_CFG = "0x01";
62+
# Configure the non-propriety NCI settings in EEPROM:
63+
# 0x28: PN_NFC_DEP_SPEED = 0x00 (Data exchange: Highest Available Bit Rates)
64+
# 0x21: PI_BIT_RATE = 0x00 (Maximum allowed bit rate: 106 Kbit/s)
65+
# 0x30: LA_BIT_FRAME_SDD = 0x08 (Bit Frame SDD value to be sent in Byte 1 of SENS_RES)
66+
# 0x31: LA_PLATFORM_CONFIG = 0x03 (Platform Configuration value to be sent in Byte 2 of SENS_RES)
67+
# 0x33: LA_NFCID1 = [ 0x04 0x03 0x02 0x01 ] ("Unique" NFCID1 ID in SENS_RES)
68+
# 0x54: LF_CON_BITR_F = 0x06 (Bit rates to listen for: Both)
69+
# 0x50: LF_PROTOCOL_TYPE = 0x02 (Protocols supported in Listen Mode for NFC-F: NFC-DEP)
70+
# 0x5B: LI_BIT_RATE = 0x00 (Maximum supported bit rate: 106 Kbit/s)
71+
# 0x60: LN_WT = 0x0E (Waiting Time NFC-DEP WT_MAX default for Initiator)
72+
# 0x80: RF_FIELD_INFO = 0x01 (Chip is allowed to emit RF Field Information Notifications)
73+
# 0x81: RF_NFCEE_ACTION = 0x01 (Chip should send trigger notification for the default set of NFCEE actions)
74+
# 0x82: NFCDEP_OP = 0x0E (NFC-DEP protocol behavior: Default flags, but also enable RTOX requests)
75+
# 0x18: PF_BIT_RATE = 0x01 (NFC-F discovery polling initial bit rate: 106 Kbit/s)
76+
NXP_CORE_CONF = ''
77+
{20, 02, 2B, 0D,
78+
28, 01, 00,
79+
21, 01, 00,
80+
30, 01, 08,
81+
31, 01, 03,
82+
33, 04, 04, 03, 02, 01,
83+
54, 01, 06,
84+
50, 01, 02,
85+
5B, 01, 00,
86+
60, 01, 0E,
87+
80, 01, 01,
88+
81, 01, 01,
89+
82, 01, 0E,
90+
18, 01, 01
91+
}
92+
'';
93+
# Configure the proprietary NXP extension to the NCI standard in EEPROM:
94+
# 0xA0 0x5E: JEWEL_RID_CFG = 0x01 (Enable sending RID to T1T on RF)
95+
# 0xA0 0x40: TAG_DETECTOR_CFG = 0x00 (Tag detector: Disable both AGC based detection and trace mode)
96+
# 0xA0 0x43: TAG_DETECTOR_FALLBACK_CNT_CFG = 0x00 (Tag detector: Disable hybrid mode, only use LPCD to initiate polling)
97+
# 0xA0 0x0F: DH_EEPROM_AREA_1 = [ 32 bytes of opaque Lenovo data ] (Custom configuration for the Lenovo customized chip firmware)
98+
# See also https://github.com/nfc-tools/libnfc/issues/455#issuecomment-2221979571
99+
NXP_CORE_CONF_EXTN = ''
100+
{20, 02, 30, 04,
101+
A0, 5E, 01, 01,
102+
A0, 40, 01, 00,
103+
A0, 43, 01, 00,
104+
A0, 0F, 20,
105+
00, 03, 1D, 01, 03, 00, 02, 00,
106+
01, 00, 01, 00, 00, 00, 00, 00,
107+
00, 00, 00, 00, 00, 00, 00, 00,
108+
00, 00, 00, 00, 00, 00, 00, 00
109+
}
110+
'';
111+
# Firmware-specific protocol configuration parameters (one byte per protocol)
112+
NXP_NFC_PROPRIETARY_CFG = "{05:FF:FF:06:81:80:70:FF:FF}";
113+
# Configure power supply of chip, use Lenovo driver configuration, which deviates a bit from the spec:
114+
# 0xA0 0x0E: PMU_CFG = [ 0x16, 0x09, 0x00 ] (VBAT1 connected to 5V, TVDD monitoring: 3.6V, TxLDO Voltage in reader and card mode: 3.3V)
115+
NXP_EXT_TVDD_CFG = "0x01";
116+
NXP_EXT_TVDD_CFG_1 = ''
117+
{20, 02, 07, 01,
118+
A0, 0E, 03, 16, 09, 00
119+
}
120+
'';
121+
# Use the default for NFA_EE_MAX_EE_SUPPORTED stack size (concerns HCI)
122+
NXP_NFC_MAX_EE_SUPPORTED = "0x00";
123+
};
124+
};
125+
126+
generateSettings =
127+
cfgName:
128+
let
129+
toKeyValueLines =
130+
obj: builtins.concatStringsSep "\n" (map (key: "${key}=${obj.${key}}") (builtins.attrNames obj));
131+
in
132+
toKeyValueLines (defaultSettings.${cfgName} // (cfg.settings.${cfgName} or { }));
133+
in
134+
{
135+
options.hardware.nfc-nci = {
136+
enable = lib.mkEnableOption "PN5xx kernel module with udev rules, libnfc-nci userland, and optional ifdnfc-nci PC/SC driver";
137+
138+
settings = lib.mkOption {
139+
default = defaultSettings;
140+
description = ''
141+
Configuration to be written to the libncf-nci configuration files.
142+
To understand the configuration format, refer to https://github.com/NXPNFCLinux/linux_libnfc-nci/tree/master/conf.
143+
'';
144+
type = lib.types.attrs;
145+
};
146+
147+
enableIFD = lib.mkOption {
148+
type = lib.types.bool;
149+
default = true;
150+
description = ''
151+
Register ifdnfc-nci as a serial reader with pcscd.
152+
'';
153+
};
154+
};
155+
156+
config = lib.mkIf cfg.enable {
157+
environment.systemPackages =
158+
[
159+
pkgs.libnfc-nci
160+
]
161+
++ lib.optionals cfg.enableIFD [
162+
pkgs.ifdnfc-nci
163+
];
164+
165+
environment.etc = {
166+
"libnfc-nci.conf".text = generateSettings "nci";
167+
"libnfc-nxp-init.conf".text = generateSettings "init";
168+
"libnfc-nxp-pn547.conf".text = generateSettings "pn54x";
169+
"libnfc-nxp-pn548.conf".text = generateSettings "pn54x";
170+
};
171+
172+
services.udev.packages = [
173+
config.boot.kernelPackages.nxp-pn5xx
174+
];
175+
176+
boot.blacklistedKernelModules = [
177+
"nxp_nci_i2c"
178+
"nxp_nci"
179+
];
180+
181+
boot.extraModulePackages = [
182+
config.boot.kernelPackages.nxp-pn5xx
183+
];
184+
185+
boot.kernelModules = [
186+
"nxp-pn5xx"
187+
];
188+
189+
services.pcscd.readerConfigs = lib.mkIf cfg.enableIFD [
190+
''
191+
FRIENDLYNAME "NFC NCI"
192+
LIBPATH ${pkgs.ifdnfc-nci}/lib/libifdnfc-nci.so
193+
CHANNELID 0
194+
''
195+
];
196+
197+
# NFC chip looses power when system goes to sleep / hibernate,
198+
# and needs to be re-initialized upon wakeup
199+
powerManagement.resumeCommands = lib.mkIf cfg.enableIFD ''
200+
systemctl restart pcscd.service
201+
'';
202+
};
203+
204+
meta.maintainers = with lib.maintainers; [ stargate01 ];
205+
}

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
./hardware/network/eg25-manager.nix
8686
./hardware/network/intel-2200bg.nix
8787
./hardware/new-lg4ff.nix
88+
./hardware/nfc-nci.nix
8889
./hardware/nitrokey.nix
8990
./hardware/onlykey/default.nix
9091
./hardware/openrazer.nix

nixos/modules/services/hardware/pcscd.nix

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
{ config, lib, pkgs, ... }:
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
27
let
38
cfg = config.services.pcscd;
4-
cfgFile = pkgs.writeText "reader.conf" config.services.pcscd.readerConfig;
9+
cfgFile = pkgs.writeText "reader.conf" (
10+
builtins.concatStringsSep "\n\n" config.services.pcscd.readerConfigs
11+
);
512

6-
package = if config.security.polkit.enable
7-
then pkgs.pcscliteWithPolkit
8-
else pkgs.pcsclite;
13+
package = if config.security.polkit.enable then pkgs.pcscliteWithPolkit else pkgs.pcsclite;
914

1015
pluginEnv = pkgs.buildEnv {
1116
name = "pcscd-plugins";
@@ -14,6 +19,20 @@ let
1419

1520
in
1621
{
22+
imports = [
23+
(lib.mkChangedOptionModule
24+
[ "services" "pcscd" "readerConfig" ]
25+
[ "services" "pcscd" "readerConfigs" ]
26+
(
27+
config:
28+
let
29+
readerConfig = lib.getAttrFromPath [ "services" "pcscd" "readerConfig" ] config;
30+
in
31+
[ readerConfig ]
32+
)
33+
)
34+
];
35+
1736
options.services.pcscd = {
1837
enable = lib.mkEnableOption "PCSC-Lite daemon, to access smart cards using SCard API (PC/SC)";
1938

@@ -24,15 +43,17 @@ in
2443
description = "Plugin packages to be used for PCSC-Lite.";
2544
};
2645

27-
readerConfig = lib.mkOption {
28-
type = lib.types.lines;
29-
default = "";
30-
example = ''
31-
FRIENDLYNAME "Some serial reader"
32-
DEVICENAME /dev/ttyS0
33-
LIBPATH /path/to/serial_reader.so
34-
CHANNELID 1
35-
'';
46+
readerConfigs = lib.mkOption {
47+
type = lib.types.listOf lib.types.lines;
48+
default = [ ];
49+
example = [
50+
''
51+
FRIENDLYNAME "Some serial reader"
52+
DEVICENAME /dev/ttyS0
53+
LIBPATH /path/to/serial_reader.so
54+
CHANNELID 1
55+
''
56+
];
3657
description = ''
3758
Configuration for devices that aren't hotpluggable.
3859
@@ -68,7 +89,10 @@ in
6889
# around it, we force the path to the cfgFile.
6990
#
7091
# https://github.com/NixOS/nixpkgs/issues/121088
71-
serviceConfig.ExecStart = [ "" "${lib.getExe package} -f -x -c ${cfgFile} ${lib.escapeShellArgs cfg.extraArgs}" ];
92+
serviceConfig.ExecStart = [
93+
""
94+
"${lib.getExe package} -f -x -c ${cfgFile} ${lib.escapeShellArgs cfg.extraArgs}"
95+
];
7296
};
7397
};
7498
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
lib,
3+
stdenv,
4+
fetchFromGitHub,
5+
pkg-config,
6+
cmake,
7+
pcsclite,
8+
libnfc-nci,
9+
}:
10+
11+
stdenv.mkDerivation (finalAttrs: {
12+
pname = "ifdnfc-nci";
13+
version = "0.2.1";
14+
15+
src = fetchFromGitHub {
16+
owner = "StarGate01";
17+
repo = "ifdnfc-nci";
18+
tag = "v${finalAttrs.version}";
19+
sha256 = "sha256-I2MNzmaxQUh4bN3Uytf2bQRthByEaFWM7c79CKZJQZA=";
20+
};
21+
22+
nativeBuildInputs = [
23+
cmake
24+
pkg-config
25+
];
26+
27+
buildInputs = [
28+
pcsclite
29+
libnfc-nci
30+
];
31+
32+
meta = {
33+
description = "PC/SC IFD Handler based on linux_libnfc-nci";
34+
homepage = "https://github.com/StarGate01/ifdnfc-nci";
35+
license = lib.licenses.gpl3Only;
36+
platforms = lib.platforms.linux;
37+
maintainers = with lib.maintainers; [ stargate01 ];
38+
};
39+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
lib,
3+
stdenv,
4+
fetchFromGitHub,
5+
autoreconfHook,
6+
pkg-config,
7+
config,
8+
debug ? config.libnfc-nci.debug or false,
9+
}:
10+
11+
stdenv.mkDerivation (finalAttrs: {
12+
pname = "libnfc-nci";
13+
version = "2.4.1-unstable-2024-08-05";
14+
15+
src = fetchFromGitHub {
16+
owner = "StarGate01";
17+
repo = "linux_libnfc-nci";
18+
rev = "7ce9c8aad0e37850a49b6d8dcc22ae5c783268e7";
19+
sha256 = "sha256-iSvDiae+A2hUok426Lj5TMn3Q9G+vH1G0jajP48PehQ=";
20+
};
21+
22+
buildInputs = [
23+
pkg-config
24+
autoreconfHook
25+
];
26+
27+
configureFlags =
28+
[
29+
"--enable-i2c"
30+
]
31+
++ lib.optionals debug [
32+
"--enable-debug"
33+
];
34+
dontStrip = debug;
35+
36+
postInstall = ''
37+
rm -rf $out/etc
38+
'';
39+
40+
meta = {
41+
description = "Linux NFC stack for NCI based NXP NFC Controllers";
42+
homepage = "https://github.com/NXPNFCLinux/linux_libnfc-nci";
43+
license = lib.licenses.asl20;
44+
maintainers = with lib.maintainers; [ stargate01 ];
45+
platforms = lib.platforms.linux;
46+
};
47+
})

0 commit comments

Comments
 (0)