commit | author | age
|
6b0ed4
|
1 |
#!/usr/bin/env python3 |
JK |
2 |
|
|
3 |
# Enable NFC on Linux (CCID/PCSCD) |
|
4 |
# Dell E7470 |
|
5 |
# Dell ControlVault2 |
|
6 |
# BCM20795 (20795A1) |
|
7 |
|
|
8 |
import binascii |
|
9 |
import logging |
|
10 |
import math |
|
11 |
import struct |
|
12 |
import sys |
|
13 |
import time |
|
14 |
import usb.core |
|
15 |
import usb.util |
|
16 |
|
|
17 |
VENDOR_ID = 0x0A5C |
|
18 |
DEVICE_ID = 0x5834 |
|
19 |
|
|
20 |
logging.basicConfig(level=logging.DEBUG) |
|
21 |
logger = logging.getLogger(__name__) |
|
22 |
|
|
23 |
def to_hex(val): |
|
24 |
return ' '.join([bytes([i]).hex() for i in val]) |
|
25 |
|
|
26 |
class BcmCommunicator: |
|
27 |
def __init__(self, device, bulk_in, bulk_out): |
|
28 |
self.logger = logging.getLogger(__name__) |
|
29 |
self.device = device |
|
30 |
self.bulk_in = bulk_in |
|
31 |
self.bulk_out = bulk_out |
|
32 |
|
|
33 |
def ctrl_transfer(self, *args, **kwargs): |
|
34 |
self.logger.debug('Control: {} {}'.format(args, kwargs)) |
|
35 |
return self.device.ctrl_transfer(*args, **kwargs) |
|
36 |
|
|
37 |
def write(self, *args, **kwargs): |
|
38 |
return self.bulk_out.write(*args, **kwargs) |
|
39 |
|
|
40 |
def read(self, *args, **kwargs): |
|
41 |
return self.bulk_in.read(*args, **kwargs) |
|
42 |
|
|
43 |
def send_packet(self, payload): |
|
44 |
packet_type = 0x01 |
|
45 |
unknown1 = 0x00 |
|
46 |
length = len(payload) |
|
47 |
|
|
48 |
packet = struct.pack('>BBH', packet_type, unknown1, length) + payload |
|
49 |
|
|
50 |
self.logger.debug('Put: {}'.format(to_hex(packet))) |
|
51 |
self.write(packet) |
|
52 |
|
|
53 |
def recv_packet(self): |
|
54 |
packet = self.read(64, timeout=5000).tobytes() |
|
55 |
tag = packet[0:2] |
|
56 |
if tag != b'\x00\x00': |
|
57 |
raise Exception('Unknown tag: {}'.format(tag.hex())) |
|
58 |
length = packet[2:4] |
|
59 |
length = struct.unpack('>H', length)[0] |
|
60 |
|
|
61 |
for i in range(0, math.ceil(length/64) - 1): |
|
62 |
packet += self.read(64).tobytes() |
|
63 |
|
|
64 |
self.logger.debug('Got: {}'.format(to_hex(packet))) |
|
65 |
return packet[4:] |
|
66 |
|
|
67 |
def talk(self, exchange): |
|
68 |
for packet in exchange: |
|
69 |
self.send_packet(bytes.fromhex(packet)) |
|
70 |
data = self.recv_packet() |
|
71 |
|
|
72 |
if data[1] == 0x61: |
|
73 |
packet = self.recv_packet() |
|
74 |
|
|
75 |
@classmethod |
|
76 |
def find(cls, vendor_id, product_id): |
|
77 |
logger = logging.getLogger(__name__) |
|
78 |
logger.info('Looking for device {:04X}:{:04X}...'.format(vendor_id, product_id)) |
|
79 |
|
|
80 |
device = usb.core.find(idVendor=vendor_id, idProduct=product_id) |
|
81 |
if device is None: |
|
82 |
raise Exception('Cannot find device {:04X}:{:04X}'.format(vendor_id, product_id)) |
|
83 |
|
|
84 |
logger.debug('Enumerating interfaces...') |
|
85 |
configuration = device.get_active_configuration() |
|
86 |
bcm_interface = None |
|
87 |
for interface in configuration: |
|
88 |
if interface.bInterfaceClass == 0xff and interface.iInterface == 0x08: |
|
89 |
bcm_interface = interface |
|
90 |
break |
|
91 |
if bcm_interface is None: |
|
92 |
raise Exception('Cannot find vendor-specific interface') |
|
93 |
logger.debug('Interface found: {}'.format(bcm_interface._str())) |
|
94 |
|
|
95 |
logger.debug('Enumerating endpoints...') |
|
96 |
bulk_in = None |
|
97 |
bulk_out = None |
|
98 |
for endpoint in bcm_interface: |
|
99 |
if endpoint.bmAttributes & usb.util._ENDPOINT_TRANSFER_TYPE_MASK == usb.util.ENDPOINT_TYPE_BULK: |
|
100 |
if endpoint.bEndpointAddress & usb.util._ENDPOINT_DIR_MASK == usb.util.ENDPOINT_IN: |
|
101 |
if bulk_in is not None: |
|
102 |
raise Exception('More than one BULK IN endpoint found!') |
|
103 |
bulk_in = endpoint |
|
104 |
logger.debug('BULK IN found: {}'.format(bulk_in._str())) |
|
105 |
if endpoint.bEndpointAddress & usb.util._ENDPOINT_DIR_MASK == usb.util.ENDPOINT_OUT: |
|
106 |
if bulk_out is not None: |
|
107 |
raise Exception('More than one BULK OUT endpoint found!') |
|
108 |
bulk_out = endpoint |
|
109 |
logger.debug('BULK OUT found: {}'.format(bulk_out._str())) |
|
110 |
|
|
111 |
if bulk_in is None: |
|
112 |
raise Exception('BULK IN endpoint not found!') |
|
113 |
if bulk_out is None: |
|
114 |
raise Exception('BULK OUT endpoint not found!') |
|
115 |
|
|
116 |
logger.debug('Returning {} object...'.format(cls.__name__)) |
|
117 |
return cls(device, bulk_in, bulk_out) |
|
118 |
|
|
119 |
turn_on_seq1 = [ |
|
120 |
"10 2f 04 00", |
|
121 |
"10 2f 1d 03 05 90 65", |
|
122 |
"10 2f 2d 00", |
|
123 |
"10 2f 11 01 f7", |
|
124 |
"01 27 fc 0c 08 00 01 00 01 00 00 00 00 00 00 00", |
|
125 |
] |
|
126 |
turn_on_seq2 = [ |
|
127 |
"10 20 00 01 01", |
|
128 |
"10 20 01 02 01 00", |
|
129 |
"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", |
|
130 |
"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", |
|
131 |
"10 20 02 06 01 b7 03 02 00 01", |
|
132 |
"10 2f 06 01 01", |
|
133 |
"10 20 02 0e 02 51 08 20 79 ff ff ff ff ff ff 58 01 07", |
|
134 |
"10 21 00 07 02 04 03 02 05 03 03", |
|
135 |
"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", |
|
136 |
"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", |
|
137 |
"10 20 02 10 05 30 01 04 31 01 00 32 01 40 38 01 00 50 01 02", |
|
138 |
"10 20 02 05 01 00 02 fa 00", |
|
139 |
"10 20 02 0b 01 c2 08 01 08 00 04 80 c3 c9 01", |
|
140 |
"10 21 03 0d 06 00 01 01 01 02 01 80 01 82 01 06 01", |
|
141 |
] |
|
142 |
|
|
143 |
def turn_on(communicator): |
|
144 |
communicator.ctrl_transfer(0x41, 0, 1, 3) |
|
145 |
communicator.talk(turn_on_seq1) |
|
146 |
communicator.ctrl_transfer(0x41, 1, 0, 3) |
|
147 |
communicator.talk(turn_on_seq2) |
|
148 |
communicator.ctrl_transfer(0x41, 1, 1, 3) |
|
149 |
|
|
150 |
def turn_off(communicator): |
|
151 |
communicator.ctrl_transfer(0x41, 1, 0, 3) |
|
152 |
communicator.ctrl_transfer(0x41, 0, 0, 3) |
|
153 |
|
|
154 |
|
|
155 |
|
|
156 |
if __name__ == "__main__": |
|
157 |
if len(sys.argv) < 2: |
|
158 |
print('Usage: {} [on|off]'.format(sys.argv[0])) |
|
159 |
sys.exit(2) |
|
160 |
|
|
161 |
communicator = BcmCommunicator.find(VENDOR_ID, DEVICE_ID) |
|
162 |
if sys.argv[1] == 'on': |
|
163 |
logger.info('Turning NFC on...') |
|
164 |
turn_on(communicator) |
|
165 |
logger.info('NFC should be turned on now!') |
|
166 |
elif sys.argv[1] == 'off': |
|
167 |
logger.info('Turning NFC off...') |
|
168 |
turn_off(communicator) |
|
169 |
logger.info('NFC should be turned off now!') |
|
170 |
else: |
|
171 |
raise Exception('Unknown option: {}'.format(sys.argv[1])) |