Add Thinkpad NXP1001 NFC support using libnfc-nci and PC/SC (#379118)

authored by misuzu.tngl.sh and committed by GitHub 45798f6e 5156904c

+375 -15
+2
nixos/doc/manual/release-notes/rl-2505.section.md
··· 113 113 114 114 - [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). 115 115 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 + 116 118 - [duckdns](https://www.duckdns.org), free dynamic DNS. Available with [services.duckdns](options.html#opt-services.duckdns.enable) 117 119 118 120 - [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).
+205
nixos/modules/hardware/nfc-nci.nix
··· 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 + }
+1
nixos/modules/module-list.nix
··· 85 85 ./hardware/network/eg25-manager.nix 86 86 ./hardware/network/intel-2200bg.nix 87 87 ./hardware/new-lg4ff.nix 88 + ./hardware/nfc-nci.nix 88 89 ./hardware/nitrokey.nix 89 90 ./hardware/onlykey/default.nix 90 91 ./hardware/openrazer.nix
+39 -15
nixos/modules/services/hardware/pcscd.nix
··· 1 - { config, lib, pkgs, ... }: 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 2 7 let 3 8 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 + ); 5 12 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; 9 14 10 15 pluginEnv = pkgs.buildEnv { 11 16 name = "pcscd-plugins"; ··· 14 19 15 20 in 16 21 { 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 + 17 36 options.services.pcscd = { 18 37 enable = lib.mkEnableOption "PCSC-Lite daemon, to access smart cards using SCard API (PC/SC)"; 19 38 ··· 24 43 description = "Plugin packages to be used for PCSC-Lite."; 25 44 }; 26 45 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 + ]; 36 57 description = '' 37 58 Configuration for devices that aren't hotpluggable. 38 59 ··· 68 89 # around it, we force the path to the cfgFile. 69 90 # 70 91 # 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 + ]; 72 96 }; 73 97 }; 74 98 }
+39
pkgs/by-name/if/ifdnfc-nci/package.nix
··· 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 + })
+47
pkgs/by-name/li/libnfc-nci/package.nix
··· 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 + })
+40
pkgs/os-specific/linux/nxp-pn5xx/default.nix
··· 1 + { 2 + lib, 3 + stdenv, 4 + fetchFromGitHub, 5 + kernel, 6 + kernelModuleMakeFlags, 7 + }: 8 + 9 + stdenv.mkDerivation (finalAttrs: { 10 + pname = "nxp-pn5xx"; 11 + version = "0.4-unstable-2025-02-08-${kernel.version}"; 12 + 13 + src = fetchFromGitHub { 14 + owner = "jr64"; 15 + repo = "nxp-pn5xx"; 16 + rev = "07411e0ce3445e7dcb970df1837f0ad74b7b0a7a"; 17 + hash = "sha256-jVkcvURFlihKW2vFvAaqzKdtexPXywRa2LkPkIhmdeU="; 18 + }; 19 + 20 + nativeBuildInputs = kernel.moduleBuildDependencies; 21 + 22 + makeFlags = kernelModuleMakeFlags ++ [ 23 + "KERNELRELEASE=${kernel.modDirVersion}" 24 + "BUILD_KERNEL_PATH=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" 25 + "INSTALL_MOD_PATH=$(out)/lib/modules/${kernel.modDirVersion}" 26 + ]; 27 + 28 + postInstall = '' 29 + mkdir -p $out/etc/udev/rules.d 30 + echo 'SUBSYSTEM=="misc", KERNEL=="pn544", MODE="0666", GROUP="dialout"' > $out/etc/udev/rules.d/99-nxp-pn5xx.rules 31 + ''; 32 + 33 + meta = { 34 + description = "NXP's NFC Open Source Kernel mode driver with ACPI configuration support"; 35 + homepage = "https://github.com/jr64/nxp-pn5xx"; 36 + license = lib.licenses.gpl2Only; 37 + maintainers = with lib.maintainers; [ stargate01 ]; 38 + platforms = lib.platforms.linux; 39 + }; 40 + })
+2
pkgs/top-level/linux-kernels.nix
··· 457 457 nvidia_x11_stable_open = nvidiaPackages.stable.open; 458 458 nvidia_x11_vulkan_beta_open = nvidiaPackages.vulkan_beta.open; 459 459 460 + nxp-pn5xx = callPackage ../os-specific/linux/nxp-pn5xx { }; 461 + 460 462 openrazer = callPackage ../os-specific/linux/openrazer/driver.nix { }; 461 463 462 464 ply = callPackage ../os-specific/linux/ply { };