Enable NFC for Linux and pcscd on Dell E7470 (and others) with ControlVault2
Jacek Kowalski
2020-07-24 f834210391b0549770947019c8bf6b5c5034d04c
commit | author | age
f83421 1 import logging
JK 2 import math
3 import struct
4 import usb.util
5
6 def to_hex(val):
7     return ' '.join([bytes([i]).hex() for i in val])
8
9 class ControlVaultCommunicator:
10     def __init__(self, device, spi_master=0x01, spi_slave=0x00, spi_crc=0x00):
11         self.logger = logging.getLogger(__name__)
12         self.device = device
13         self.bulk_in, self.bulk_out = self._find_endpoints()
14
15         self.spi_master = spi_master
16         self.spi_slave = spi_slave
17         self.spi_crc = spi_crc
18         self.spi_slave_prefix = struct.pack('>BB', self.spi_slave, self.spi_crc)
19
20     def ctrl_transfer(self, *args, **kwargs):
21         self.logger.debug('Control: {} {}'.format(args, kwargs))
22         return self.device.ctrl_transfer(*args, **kwargs)
23
24     def write(self, *args, **kwargs):
25         return self.bulk_out.write(*args, **kwargs)
26
27     def read(self, *args, **kwargs):
28         return self.bulk_in.read(*args, **kwargs)
29
30     def send_packet(self, payload):
31         length = len(payload)
32         packet = struct.pack('>BBH', self.spi_master, self.spi_crc, length) + payload
33         self.logger.debug('Put: {}'.format(to_hex(packet)))
34         self.write(packet)
35
36     def recv_packet(self):
37         packet = self.read(64, timeout=5000).tobytes()
38         tag = packet[0:2]
39         if tag != self.spi_slave_prefix:
40             raise Exception('Unknown tag: {}'.format(tag.hex()))
41         length = packet[2:4]
42         length = struct.unpack('>H', length)[0]
43
44         for i in range(0, math.ceil(length/64) - 1):
45             packet += self.read(64).tobytes()
46
47         self.logger.debug('Got: {}'.format(to_hex(packet)))
48         return packet[4:]
49
50     def talk(self, exchange):
51         for packet in exchange:
52             self.send_packet(bytes.fromhex(packet))
53             data = self.recv_packet()
54
55             if data[1] == 0x61:
56                 packet = self.recv_packet()
57
58     def _find_endpoints(self):
59         self.logger.debug('Enumerating interfaces...')
60         configuration = self.device.get_active_configuration()
61         bcm_interface = None
62         for interface in configuration:
63             if interface.bInterfaceClass == 0xff:
64                 if bcm_interface is not None:
65                     raise Exception('More than one vendor-specific interface found!')
66                 bcm_interface = interface
67         if bcm_interface is None:
68             raise Exception('Cannot find vendor-specific interface')
69         self.logger.debug('Interface found: {}'.format(bcm_interface._str()))
70
71         self.logger.debug('Enumerating endpoints...')
72         bulk_in = None
73         bulk_out = None
74         for endpoint in bcm_interface:
75             if endpoint.bmAttributes & usb.util._ENDPOINT_TRANSFER_TYPE_MASK == usb.util.ENDPOINT_TYPE_BULK:
76                 if endpoint.bEndpointAddress & usb.util._ENDPOINT_DIR_MASK == usb.util.ENDPOINT_IN:
77                     if bulk_in is not None:
78                         raise Exception('More than one BULK IN endpoint found!')
79                     bulk_in = endpoint
80                     self.logger.debug('BULK IN found: {}'.format(bulk_in._str()))
81                 if endpoint.bEndpointAddress & usb.util._ENDPOINT_DIR_MASK == usb.util.ENDPOINT_OUT:
82                     if bulk_out is not None:
83                         raise Exception('More than one BULK OUT endpoint found!')
84                     bulk_out = endpoint
85                     self.logger.debug('BULK OUT found: {}'.format(bulk_out._str()))
86
87         if bulk_in is None:
88             raise Exception('BULK IN endpoint not found!')
89         if bulk_out is None:
90             raise Exception('BULK OUT endpoint not found!')
91
92         self.logger.debug('Endpoint discovery successful.')
93         return bulk_in, bulk_out