5ace89789bb25beefa3c70bcb64fc2d40a744281
[gnuk/gnuk.git] / tool / hub_ctrl.py
1 #! /usr/bin/python
2
3 """
4 hub_ctrl.py - a tool to control port power/led of USB hub
5
6 Copyright (C) 2006, 2011, 2016 Free Software Initiative of Japan
7
8 Author: NIIBE Yutaka  <gniibe@fsij.org>
9
10 This file is a part of Gnuk, a GnuPG USB Token implementation.
11
12 Gnuk is free software: you can redistribute it and/or modify it
13 under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 Gnuk is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
20 License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 """
25
26 from __future__ import print_function
27 import usb
28
29 USB_RT_HUB              =       (usb.TYPE_CLASS | usb.RECIP_DEVICE)
30 USB_RT_PORT             =       (usb.TYPE_CLASS | usb.RECIP_OTHER)
31 USB_PORT_FEAT_RESET     =       4
32 USB_PORT_FEAT_POWER     =       8
33 USB_PORT_FEAT_INDICATOR =       22
34 USB_DIR_IN              =       0x80             # device to host
35
36 COMMAND_SET_NONE  = 0
37 COMMAND_SET_LED   = 1
38 COMMAND_SET_POWER = 2
39
40 HUB_LED_GREEN     = 2
41
42 def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None):
43     number_of_hubs_with_feature = 0
44     hubs = []
45     busses = usb.busses()
46     if not busses:
47         raise ValueError("can't access USB")
48
49     for bus in busses:
50         devices = bus.devices
51         for dev in devices:
52             if dev.deviceClass != usb.CLASS_HUB:
53                 continue
54
55             printout_enable = 0
56             if (listing
57                 or (verbose
58                     and ((bus.dirname == busnum and dev.devnum == devnumd)
59                          or hub == number_of_hubs_with_feature))):
60                 printout_enable = 1
61
62             uh = dev.open()
63
64             desc = None
65             try:
66                 # Get USB Hub descriptor
67                 desc = uh.controlMsg(requestType = USB_DIR_IN | USB_RT_HUB, 
68                                      request = usb.REQ_GET_DESCRIPTOR,
69                                      value = usb.DT_HUB << 8,
70                                      index = 0, buffer = 1024, timeout = 1000)
71             finally:
72                 del uh
73
74             if not desc:
75                 continue
76
77             # desc[3] is lower byte of wHubCharacteristics
78             if (desc[3] & 0x80) == 0 and (desc[3] & 0x03) >= 2:
79                 # Hub doesn't have features of controling port power/indicator
80                 continue
81
82             if printout_enable:
83                 print("Hub #%d at %s:%03d" % (number_of_hubs_with_feature,
84                                               bus.dirname, dev.devnum))
85                 if (desc[3] & 0x03) == 0:
86                     print(" INFO: ganged power switching.")
87                 elif (desc[3] & 0x03) == 1:
88                     print(" INFO: individual power switching.")
89                 elif (desc[3] & 0x03) == 2 or (desc[3] & 0x03) == 3:
90                     print(" WARN: no power switching.")
91
92                 if (desc[3] & 0x80) == 0:
93                     print(" WARN: Port indicators are NOT supported.")
94
95             hubs.append({ 'busnum' : bus.dirname, 'devnum' : dev.devnum,
96                           'indicator_support' : (desc[3] & 0x80) == 0x80,
97                           'dev' : dev, 'num_ports' : desc[2] })
98             number_of_hubs_with_feature += 1
99
100     return hubs
101
102 def hub_port_status(handle, num_ports):
103     print(" Hub Port Status:")
104     for i in range(num_ports):
105         port = i + 1
106         status = handle.controlMsg(requestType = USB_RT_PORT | usb.ENDPOINT_IN, 
107                                    request = usb.REQ_GET_STATUS,
108                                    value = 0,
109                                    index = port, buffer = 4,
110                                    timeout = 1000)
111
112         print("   Port %d: %02x%02x.%02x%02x" % (port, status[3], status[2],
113                                                  status[1], status[0]),)
114         if status[1] & 0x10:
115             print(" indicator", end='')
116         if status[1] & 0x08:
117             print(" test" , end='')
118         if status[1] & 0x04:
119             print(" highspeed", end='')
120         if status[1] & 0x02:
121             print(" lowspeed", end='')
122         if status[1] & 0x01:
123             print(" power", end='')
124
125         if status[0] & 0x10:
126             print(" RESET", end='')
127         if status[0] & 0x08:
128             print(" oc", end='')
129         if status[0] & 0x04:
130             print(" suspend", end='')
131         if status[0] & 0x02:
132             print(" enable", end='')
133         if status[0] & 0x01:
134             print(" connect", end='')
135
136         print()
137
138 import sys
139
140 COMMAND_SET_NONE  = 0
141 COMMAND_SET_LED   = 1
142 COMMAND_SET_POWER = 2
143 HUB_LED_GREEN  = 2
144
145 def usage(progname):
146     print("""Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}]
147           [-P PORT] [{-p [VALUE]|-l [VALUE]}]
148 """ % progname, file=sys.stderr)
149
150 def exit_with_usage(progname):
151     usage(progname)
152     exit(1)
153
154 if __name__ == '__main__':
155     busnum = None
156     devnum = None
157     listing = False
158     verbose = False
159     hub = None
160     port = 1
161     cmd = COMMAND_SET_NONE
162
163     if len(sys.argv) == 1:
164         listing = True
165     else:
166         try:
167             while len(sys.argv) >= 2:
168                 option = sys.argv[1]
169                 sys.argv.pop(1)
170                 if option == '-h':
171                     if busnum != None or devnum != None:
172                         exit_with_usage(sys.argv[0])
173                     hub = int(sys.argv[1])
174                     sys.argv.pop(1)
175                 elif option == '-b':
176                     busnum = int(sys.argv[1])
177                     sys.argv.pop(1)
178                 elif option == '-d':
179                     devnum = int(sys.argv[1])
180                     sys.argv.pop(1)
181                 elif option == '-P':
182                     port = int(sys.argv[1])
183                     sys.argv.pop(1)
184                 elif option == '-l':
185                     if cmd != COMMAND_SET_NONE:
186                         exit_with_usage(sys.argv[0])
187                     if len(sys.argv) > 1:
188                         value = int(sys.argv[1])
189                         sys.argv.pop(1)
190                     else:
191                         value = HUB_LED_GREEN
192                     cmd = COMMAND_SET_LED
193                 elif option == '-p':
194                     if cmd != COMMAND_SET_NONE:
195                         exit_with_usage(sys.argv[0])
196                     if len(sys.argv) > 1:
197                         value = int(sys.argv[1])
198                         sys.argv.pop(1)
199                     else:
200                         value = 0
201                         cmd = COMMAND_SET_POWER
202                 elif option == '-v':
203                     verbose = True
204                     if len(sys.argv) == 1:
205                         listing = True
206                 else:
207                     exit_with_usage(sys.argv[0])
208         except:
209             exit_with_usage(sys.argv[0])
210
211     if ((busnum != None and devnum == None)
212         or (busnum == None and devnum != None)):
213         exit_with_usage(sys.argv[0])
214
215     if hub == None and busnum == None:
216         hub = 0                 # Default hub = 0
217
218     if cmd == COMMAND_SET_NONE:
219         cmd = COMMAND_SET_POWER
220
221     hubs = find_hubs(listing, verbose, busnum, devnum, hub)
222     if len(hubs) == 0:
223         print("No hubs found.", file=sys.stderr)
224         exit(1)
225     if listing:
226         exit(0)
227
228     if hub == None:
229         for h in hubs:
230             if h['busnum'] == busnum and h['devnum'] == devnum:
231                 dev_hub = h['dev']
232                 nports = h['num_ports']
233     else:
234         dev_hub = hubs[hub]['dev']
235         nports = hubs[hub]['num_ports']
236
237     uh = dev_hub.open()
238     if cmd == COMMAND_SET_POWER:
239         feature = USB_PORT_FEAT_POWER
240         index = port
241         if value:
242             request = usb.REQ_SET_FEATURE
243         else:
244             request = usb.REQ_CLEAR_FEATURE
245     else:
246         request = usb.REQ_SET_FEATURE
247         feature = USB_PORT_FEAT_INDICATOR
248         index = (value << 8) | port
249     if verbose:
250         print("Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d) " % (request, feature, index))
251
252     uh.controlMsg(requestType = USB_RT_PORT, request = request, value = feature,
253                   index = index, buffer = None, timeout = 1000)
254     if verbose:
255         hub_port_status(uh,nports)
256
257     del uh