-
Notifications
You must be signed in to change notification settings - Fork 12
/
tmdrv.py
executable file
·131 lines (117 loc) · 3.9 KB
/
tmdrv.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env python3
# Copyright 2016, 2017 Andrew Conrad
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Tool to initialize Thrustmaster racing wheels."""
import argparse
import time
import tmdrv_devices
import usb1
from importlib import import_module
from os import path
from subprocess import check_call, CalledProcessError
device_list = ['thrustmaster_t500rs', 'thrustmaster_tmx', 'thrustmaster_tx', 'thrustmaster_tsxw']
_context = usb1.USBContext()
def initialize(device_name='thrustmaster_tx'):
try:
device = import_module('tmdrv_devices.' + device_name)
except ModuleNotFoundError:
print('Device name "' + device_name + '" is invalid.')
raise
try:
device
except UnboundLocalError:
print('Device name "' + device_name + '" is invalid.')
raise
# Send all control packets for initialization
for m in device.control:
try:
_control_init(
device.idVendor, device.idProduct[m['step'] - 1],
m['request_type'],
m['request'],
m['value'],
m['index'],
m['data'],
)
except usb1.USBErrorNotFound:
print('Error getting handle for device {:0=4x}:{:0=4x} ({} Step {}).'.format(device.idVendor, device.idProduct[m['step']-1], device.name, m['step']))
raise
except usb1.USBErrorNoDevice:
# Caught when device switches modes
pass
except usb1.USBErrorPipe:
# Possibly caught when device switches modes on older libusb
pass
except usb1.USBErrorIO:
# Possibly caught when device switches modes on newer
# libusb. This still has to be investigated, there might
# be another issue going on here.
pass
# Wait for device to switch
connected = False
while not connected:
handle = _context.openByVendorIDAndProductID(
device.idVendor, device.idProduct[m['step']],
)
if handle is not None:
connected = True
# Load configuration to remove deadzones
if device.jscal is not None:
dev_path = '/dev/input/by-id/' + device.dev_by_id
# Sometimes the device symlink is not ready in time, so we wait
n = 9
while not path.islink(dev_path):
if n > 0:
time.sleep(.5)
n -= 1
else:
print('Device "{}" not found, skipping device calibration'.format(dev_path))
raise FileNotFoundError
_jscal(device.jscal, dev_path)
def _jscal(configuration, device_file):
try:
check_call(['jscal', '-s', configuration, device_file])
except FileNotFoundError:
print('jscal not found, skipping device calibration.')
except CalledProcessError as err:
print('jscal non-zero exit code {}, device may not be calibrated'.format(str(err)[-1]))
def _control_init(idVendor, idProduct, request_type, request, value, index, data):
handle = _context.openByVendorIDAndProductID(
idVendor, idProduct,
)
if handle is None:
raise usb1.USBErrorNotFound('Device not found or wrong permissions')
handle.setAutoDetachKernelDriver(True)
handle.claimInterface(0)
# Send control packet that will switch modes
handle.controlWrite(
request_type,
request,
value,
index,
data,
)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-d', '--device', default='thrustmaster_tx',
help='Specify device to use')
parser.add_argument('-D', '--supported-devices', action='store_true',
help='List all supported devices')
args = parser.parse_args()
if args.supported_devices:
for d in device_list:
print(d)
else:
initialize(args.device)