update tool/hub_ctrl.py from Gnuk
[gnuk/neug.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 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 import usb
27
28 USB_RT_HUB              =       (usb.TYPE_CLASS | usb.RECIP_DEVICE)
29 USB_RT_PORT             =       (usb.TYPE_CLASS | usb.RECIP_OTHER)
30 USB_PORT_FEAT_RESET     =       4
31 USB_PORT_FEAT_POWER     =       8
32 USB_PORT_FEAT_INDICATOR =       22
33 USB_DIR_IN              =       0x80             # device to host
34
35 COMMAND_SET_NONE  = 0
36 COMMAND_SET_LED   = 1
37 COMMAND_SET_POWER = 2
38
39 HUB_LED_GREEN     = 2
40
41 def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None):
42     number_of_hubs_with_feature = 0
43     hubs = []
44     busses = usb.busses()
45     if not busses:
46         raise ValueError, "can't access USB"
47
48     for bus in busses:
49         devices = bus.devices
50         for dev in devices:
51             if dev.deviceClass != usb.CLASS_HUB:
52                 continue
53
54             printout_enable = 0
55             if (listing
56                 or (verbose
57                     and ((bus.dirname == busnum and dev.devnum == devnumd)
58                          or hub == number_of_hubs_with_feature))):
59                 printout_enable = 1
60
61             uh = dev.open()
62
63             desc = None
64             try:
65                 # Get USB Hub descriptor
66                 desc = uh.controlMsg(requestType = USB_DIR_IN | USB_RT_HUB, 
67                                      request = usb.REQ_GET_DESCRIPTOR,
68                                      value = usb.DT_HUB << 8,
69                                      index = 0, buffer = 1024, timeout = 1000)
70             finally:
71                 del uh
72
73             if not desc:
74                 continue
75
76             # desc[3] is lower byte of wHubCharacteristics
77             if (desc[3] & 0x80) == 0 and (desc[3] & 0x03) >= 2:
78                 # Hub doesn't have features of controling port power/indicator
79                 continue
80
81             if printout_enable:
82                 print "Hub #%d at %s:%03d" % (number_of_hubs_with_feature,
83                                               bus.dirname, dev.devnum)
84                 if (desc[3] & 0x03) == 0:
85                     print " INFO: ganged power switching."
86                 elif (desc[3] & 0x03) == 1:
87                     print " INFO: individual power switching."
88                 elif (desc[3] & 0x03) == 2 or (desc[3] & 0x03) == 3:
89                     print " WARN: no power switching."
90
91                 if (desc[3] & 0x80) == 0:
92                     print " WARN: Port indicators are NOT supported."
93
94             hubs.append({ 'busnum' : bus.dirname, 'devnum' : dev.devnum,
95                           'indicator_support' : (desc[3] & 0x80) == 0x80,
96                           'dev' : dev, 'num_ports' : desc[2] })
97             number_of_hubs_with_feature += 1
98
99     return hubs
100
101 def hub_port_status(handle, num_ports):
102     print " Hub Port Status:"
103     for i in range(num_ports):
104         port = i + 1
105         status = handle.controlMsg(requestType = USB_RT_PORT | usb.ENDPOINT_IN, 
106                                    request = usb.REQ_GET_STATUS,
107                                    value = 0,
108                                    index = port, buffer = 4,
109                                    timeout = 1000)
110
111         print "   Port %d: %02x%02x.%02x%02x" % (port, status[3], status[2],
112                                                  status[1], status[0]),
113         if status[1] & 0x10:
114             print " indicator", 
115         if status[1] & 0x08:
116             print " test" ,
117         if status[1] & 0x04:
118             print " highspeed",
119         if status[1] & 0x02:
120             print " lowspeed",
121         if status[1] & 0x01:
122             print " power",
123
124         if status[0] & 0x10:
125             print " RESET",
126         if status[0] & 0x08:
127             print " oc",
128         if status[0] & 0x04:
129             print " suspend",
130         if status[0] & 0x02:
131             print " enable",
132         if status[0] & 0x01:
133             print " connect",
134
135         print 
136
137 import sys
138
139 COMMAND_SET_NONE  = 0
140 COMMAND_SET_LED   = 1
141 COMMAND_SET_POWER = 2
142 HUB_LED_GREEN  = 2
143
144 def usage(progname):
145     print >> sys.stderr, """Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}]
146           [-P PORT] [{-p [VALUE]|-l [VALUE]}]
147 """ % progname
148
149 def exit_with_usage(progname):
150     usage(progname)
151     exit(1)
152
153 if __name__ == '__main__':
154     busnum = None
155     devnum = None
156     listing = False
157     verbose = False
158     hub = None
159     port = 1
160     cmd = COMMAND_SET_NONE
161
162     if len(sys.argv) == 1:
163         listing = True
164     else:
165         try:
166             while len(sys.argv) >= 2:
167                 option = sys.argv[1]
168                 sys.argv.pop(1)
169                 if option == '-h':
170                     if busnum != None or devnum != None:
171                         exit_with_usage(sys.argv[0])
172                     hub = int(sys.argv[1])
173                     sys.argv.pop(1)
174                 elif option == '-b':
175                     busnum = int(sys.argv[1])
176                     sys.argv.pop(1)
177                 elif option == '-d':
178                     devnum = int(sys.argv[1])
179                     sys.argv.pop(1)
180                 elif option == '-P':
181                     port = int(sys.argv[1])
182                     sys.argv.pop(1)
183                 elif option == '-l':
184                     if cmd != COMMAND_SET_NONE:
185                         exit_with_usage(sys.argv[0])
186                     if len(sys.argv) > 1:
187                         value = int(sys.argv[1])
188                         sys.argv.pop(1)
189                     else:
190                         value = HUB_LED_GREEN
191                     cmd = COMMAND_SET_LED
192                 elif option == '-p':
193                     if cmd != COMMAND_SET_NONE:
194                         exit_with_usage(sys.argv[0])
195                     if len(sys.argv) > 1:
196                         value = int(sys.argv[1])
197                         sys.argv.pop(1)
198                     else:
199                         value = 0
200                         cmd = COMMAND_SET_POWER
201                 elif option == '-v':
202                     verbose = True
203                     if len(sys.argv) == 1:
204                         listing = True
205                 else:
206                     exit_with_usage(sys.argv[0])
207         except:
208             exit_with_usage(sys.argv[0])
209
210     if ((busnum != None and devnum == None)
211         or (busnum == None and devnum != None)):
212         exit_with_usage(sys.argv[0])
213
214     if hub == None and busnum == None:
215         hub = 0                 # Default hub = 0
216
217     if cmd == COMMAND_SET_NONE:
218         cmd = COMMAND_SET_POWER
219
220     hubs = find_hubs(listing, verbose, busnum, devnum, hub)
221     if len(hubs) == 0:
222         print >> sys.stderr, "No hubs found."
223         exit(1)
224     if listing:
225         exit(0)
226
227     if hub == None:
228         for h in hubs:
229             if h['busnum'] == busnum and h['devnum'] == devnum:
230                 dev_hub = h['dev']
231                 nports = h['num_ports']
232     else:
233         dev_hub = hubs[hub]['dev']
234         nports = hubs[hub]['num_ports']
235
236     uh = dev_hub.open()
237     if cmd == COMMAND_SET_POWER:
238         feature = USB_PORT_FEAT_POWER
239         index = port
240         if value:
241             request = usb.REQ_SET_FEATURE
242         else:
243             request = usb.REQ_CLEAR_FEATURE
244     else:
245         request = usb.REQ_SET_FEATURE
246         feature = USB_PORT_FEAT_INDICATOR
247         index = (value << 8) | port
248     if verbose:
249         print "Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d) " % (request, feature, index)
250
251     uh.controlMsg(requestType = USB_RT_PORT, request = request, value = feature,
252                   index = index, buffer = None, timeout = 1000)
253     if verbose:
254         hub_port_status(uh,nports)
255
256     del uh