Enable NFC for Linux and pcscd on Dell E7470 (and others) with ControlVault2
6b0ed423385af7421416094bf09bf3c0cc69ff19..ec8c6648819d21167a67817c0e6fa5c9937ec289
2019-03-25 Jacek Kowalski
Add README.md
ec8c66 diff | tree
2019-03-25 Jacek Kowalski
Annotate traffic
74ff7b diff | tree
2019-03-25 Jacek Kowalski
Check if there is only one vendor-specific interface
36fd63 diff | tree
1 files added
2 files modified
214 ■■■■ changed files
README.md 44 ●●●●● patch | view | raw | blame | history
bcm20795.py 3 ●●●● patch | view | raw | blame | history
traffic.txt 167 ●●●● patch | view | raw | blame | history
README.md
New file
@@ -0,0 +1,44 @@
# Enable NFC on Dell ControlVault2
## Introduction
Recent Dell laptops - i.e. E7470 - contain integrated Broadcom NFC (contactless), (contacted) chip card and fingerprint reader (USB 0a5c:5834 - Broadcom BCM5880 USH).
The contacted card reader works out of the box on Linux, yet NFC reader does not - no RF field is present.
Instructions from [blog.g3rt.nl](https://blog.g3rt.nl/enable-dell-nfc-contactless-reader.html) are not applicable: the ushdiag.exe does not recognize the device at all.
This project aims to enable Linux to read NFC cards the same way Windows does.
## Usage
1. Clone the repository.
1. Install python3 and python3-usb.
1. Run: `./bcm20795.py on`
1. Run `pcsc_scan` or whatever you prefer.
1. Enjoy!
To disable the reader replace `on` with `off`.
## Supported devices
Currently only the following devices were tested and are known to work:
* `0a5c:5834`
Firmware update (done during driver installation on Windows) may be required.
## How it works?
Python script sends the same sequence of commands the Windows driver does. The traffic was sniffed using USBPcap and Wireshark (kudos to [~jkramarz](https://github.com/jkramarz) for that).
The data is sent as-is and responses are read, but no error-checking is done.
The semi-annotated traffic dump is available as [traffic.txt](traffic.txt) - feel free to decode it further!
The communication protocol is based on NCI (NFC Controller Interface). Unfortunately the specs are not freely available and some proprietary extensions are used. libnfc-nci and kernel sources were used to decode some structs.
## References
* https://android.googlesource.com/platform/external/libnfc-nci/ - the driver of NFC component (BCM 20795),
* https://blog.g3rt.nl/enable-dell-nfc-contactless-reader.html - this does not work on E7470.
bcm20795.py
@@ -86,8 +86,9 @@
        bcm_interface = None
        for interface in configuration:
            if interface.bInterfaceClass == 0xff and interface.iInterface == 0x08:
                if bcm_interface is not None:
                    raise Exception('More than one vendor-specific interface found!')
                bcm_interface = interface
                break
        if bcm_interface is None:
            raise Exception('Cannot find vendor-specific interface')
        logger.debug('Interface found: {}'.format(bcm_interface._str()))
traffic.txt
@@ -8,69 +8,152 @@
# CTRL 0x41 0x00 0x0001 0x0003
  TAG?- LEN-- VALUE
              ADDR
  TAG?- LEN--  VALUE
               ADDR
               10 NCI_COMMAND
> 01 00 00 04 10 2f 04 00
< 00 00 00 28 10 4f 04 24 00 44 65 63 20 31 33 20 32 30 31 33 32 32 3a 30 35 3a 30 36 00 01 0c 22 a1 95 07 02 07 32 30 37 39 35 41 31
> 01 00 00 04  10 2f 04  00
                  2f = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
< 00 00 00 28  10 4f 04  24 00 44 65 63 20 31 33 20 32 30 31 33 32 32 3a 30 35 3a 30 36 00 01 0c 22 a1 95 07 02 07 32 30 37 39 35 41 31
                  4f = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
> 01 00 00 07 10 2f 1d 03 05 90 65
< 00 00 00 05 10 4f 1d 01 01
> 01 00 00 07  10 2f 1d  03 05 90 65
                  2f = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
< 00 00 00 05  10 4f 1d  01 01 07 32 30 37 39 35 41 31
                  4f = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
> 01 00 00 04 10 2f 2d 00
< 00 00 00 27 10 4f 2d 23 00 00 00 07 32 30 37 39 35 41 31 00 00 00 00 00 00 00 00 00 71 06 05 00 00 00 e4 47 0f 02 db 40 00 00 00
> 01 00 00 04  10 2f 2d  00
                  2f = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
< 00 00 00 27  10 4f 2d  23 00 00 00 07 32 30 37 39 35 41 31 00 00 00 00 00 00 00 00 00 71 06 05 00 00 00 e4 47 0f 02 db 40 00 00 00
                  4f = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
> 01 00 00 05 10 2f 11 01 f7
< 00 00 00 05 10 4f 11 01 00
> 01 00 00 05  10 2f 11  01 f7
                  2f = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
< 00 00 00 05  10 4f 11  01 00
                  4f = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
> 01 00 00 10 01 27 fc 0c 08 00 01 00 01 00 00 00 00 00 00 00
< 00 00 00 07 04 0e 04 01 27 fc 00
> 01 00 00 10  01 27 fc  0c 08 00 01 00 01 00 00 00 00 00 00 00
< 00 00 00 07  04 0e  04 01 27 fc 00
# CTRL 0x41 0x01 0x0000 0x0003
> 01 00 00 05 10 20 00 01 01
< 00 00 00 07 10 40 00 03 00 11 01
> 01 00 00 05  10 20 00  01 01
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     00 = NCI_MSG_CORE_RESET
                         01 = NCI_CORE_PARAM_SIZE_RESET
                            01 = NCI_RESET_TYPE_RESET_CFG
< 00 00 00 07  10 40 00  03 00 11 01
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     00 = NCI_MSG_CORE_RESET
                         03 = NCI_CORE_PARAM_SIZE_RESET_RSP
                            00 = NCI_STATUS_OK
                               11 = NCI_VERSION (1.1)
                                  01 = NCI_RESET_STATUS_RESET_CFG
> 01 00 00 06 10 20 01 02 01 00
< 00 00 00 1f 10 40 01 1b 00 03 1e 03 01 0a 00 01 02 03 81 84 83 04 82 05 0a 00 02 f7 3d 00 2e 02 01 0c 22
> 01 00 00 06  10 20 01  02 01 00
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     01 = NCI_MSG_CORE_INIT
                         02 = size
< 00 00 00 1f  10 40 01  1b 00 03 1e 03 01 0a 00 01 02 03 81 84 83 04 82 05 0a 00 02 f7 3d 00 2e 02 01 0c 22
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     01 = NCI_MSG_CORE_INIT
                          1b = size
                            00 = NCI_STATUS_OK
> 01 00 00 6b 10 20 02 67 01 b9 64 01 00 ff ff 50 00 8b 13 00 10 00 06 00 00 00 00 00 ff 00 00 00 ff 00 00 04 00 00 00 00 03 00 00 00 03 00 0c 00 00 0d 00 00 00 00 00 00 00 00 00 00 33 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 02 53 3b 0f 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 6b  10 20 02  67 01 b9 64 01 00 ff ff 50 00 8b 13 00 10 00 06 00 00 00 00 00 ff 00 00 00 ff 00 00 04 00 00 00 00 03 00 00 00 03 00 0c 00 00 0d 00 00 00 00 00 00 00 00 00 00 33 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 02 53 3b 0f 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 94 10 20 02 90 0a ca 05 00 00 00 00 2c 80 01 01 b0 05 01 03 03 03 08 b5 03 01 03 ff c9 0d 24 00 00 00 01 00 bb 00 e4 00 0a 01 02 d6 0d 01 02 00 00 00 00 00 01 00 01 5a 00 8a b2 02 e8 03 c8 1e 06 1f 00 0a 00 30 00 04 24 00 1c 00 75 00 77 00 76 00 1c 00 03 00 0a 00 56 01 00 00 40 04 d7 01 07 dd 32 00 00 00 29 16 08 08 06 04 00 00 00 1f 27 0a 6d 20 00 52 20 00 00 00 01 85 00 00 32 1f 00 00 02 0a 16 00 02 55 55 55 55 55 55 55 55 55 55 55 55 55 1e
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 94  10 20 02  90 0a ca 05 00 00 00 00 2c 80 01 01 b0 05 01 03 03 03 08 b5 03 01 03 ff c9 0d 24 00 00 00 01 00 bb 00 e4 00 0a 01 02 d6 0d 01 02 00 00 00 00 00 01 00 01 5a 00 8a b2 02 e8 03 c8 1e 06 1f 00 0a 00 30 00 04 24 00 1c 00 75 00 77 00 76 00 1c 00 03 00 0a 00 56 01 00 00 40 04 d7 01 07 dd 32 00 00 00 29 16 08 08 06 04 00 00 00 1f 27 0a 6d 20 00 52 20 00 00 00 01 85 00 00 32 1f 00 00 02 0a 16 00 02 55 55 55 55 55 55 55 55 55 55 55 55 55 1e
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 0a 10 20 02 06 01 b7 03 02 00 01
< 00 00 00 05 10 61 07 01 00
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 0a  10 20 02  06 01 b7 03 02 00 01
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 05  10 61 07  01 00
                  61 = NCI_MTS_NTF | NCI_PBF_NO_OR_LAST | NCI_GID_RF_MANAGE
                     07 = NCI_MSG_RF_FIELD
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 05 10 2f 06 01 01
< 00 00 00 05 10 4f 06 01 00
> 01 00 00 05  10 2f 06  01 01
                  2f = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
< 00 00 00 05  10 4f 06  01 00
                  4f = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_PROP
> 01 00 00 12 10 20 02 0e 02 51 08 20 79 ff ff ff ff ff ff 58 01 07
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 12  10 20 02  0e 02 51 08 20 79 ff ff ff ff ff ff 58 01 07
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 0b 10 21 00 07 02 04 03 02 05 03 03
< 00 00 00 05 10 41 00 01 00
> 01 00 00 0b  10 21 00  07 02 04 03 02 05 03 03
                  21 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_RF_MANAGE
                     00 = NCI_MSG_RF_DISCOVER_MAP
                         07 = size
                            02 = num_mapping_configs
                               04 = NCI_PROTOCOL_ISO_DEP
                                  03 = NCI_INTERFACE_MODE_POLL_N_LISTEN
                                     02 = NCI_INTERFACE_ISO_DEP
                                        05 = NCI_PROTOCOL_NFC_DEP
                                           03 = NCI_INTERFACE_MODE_POLL_N_LISTEN
                                              03 = NCI_INTERFACE_NFC_DEP
< 00 00 00 05  10 41 00  01 00
                  41 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_RF_MANAGE
                     00 = NCI_MSG_RF_DISCOVER_MAP
                         01 = NCI_DISCOVER_PARAM_SIZE_RSP
> 01 00 00 1b 10 20 02 17 01 29 14 46 66 6d 01 01 11 02 02 07 ff 03 02 00 13 04 01 64 07 01 03
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 1b  10 20 02  17 01 29 14 46 66 6d 01 01 11 02 02 07 ff 03 02 00 13 04 01 64 07 01 03
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 1e 10 20 02 1a 02 61 14 46 66 6d 01 01 11 02 02 07 ff 03 02 00 13 04 01 64 07 01 03 60 01 07
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 1e  10 20 02  1a 02 61 14 46 66 6d 01 01 11 02 02 07 ff 03 02 00 13 04 01 64 07 01 03 60 01 07
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 14 10 20 02 10 05 30 01 04 31 01 00 32 01 40 38 01 00 50 01 02
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 14  10 20 02  10 05 30 01 04 31 01 00 32 01 40 38 01 00 50 01 02
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 09 10 20 02 05 01 00 02 fa 00
< 00 00 00 06 10 40 02 02 00 00
> 01 00 00 09  10 20 02  05 01 00 02 fa 00
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
                             nfc_hal_pre_discover_cfg
> 01 00 00 0f 10 20 02 0b 01 c2 08 01 08 00 04 80 c3 c9 01
< 00 00 00 06 10 40 02 02 00 00
                               nfc_hal_pre_discover_cfg
> 01 00 00 0f  10 20 02  0b 01 c2 08 01 08 00 04 80 c3 c9 01
                  20 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
< 00 00 00 06  10 40 02  02 00 00
                  40 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_CORE
                     02 = NCI_MSG_CORE_SET_CONFIG
> 01 00 00 11 10 21 03 0d 06 00 01 01 01 02 01 80 01 82 01 06 01
< 00 00 00 05 10 41 03 01 00
> 01 00 00 11  10 21 03  0d 06 00 01 01 01 02 01 80 01 82 01 06 01
                  21 = NCI_MTS_CMD | NCI_PBF_NO_OR_LAST | NCI_GID_RF_MANAGE
                     03 = NCI_MSG_RF_DISCOVER
< 00 00 00 05  10 41 03  01 00
                  41 = NCI_MTS_RSP | NCI_PBF_NO_OR_LAST | NCI_GID_RF_MANAGE
                     03 = NCI_MSG_RF_DISCOVER
# CTRL 0x41 0x01 0x0001 0x0003