3 # Contains class definitions for USBDevice and USBDeviceRequest.
8 name = "generic device"
10 def __init__(self, maxusb_app, device_class, device_subclass,
11 protocol_rel_num, max_packet_size_ep0, vendor_id, product_id,
12 device_rev, manufacturer_string, product_string,
13 serial_number_string, configurations=[], descriptors={},
15 self.maxusb_app = maxusb_app
16 self.verbose = verbose
20 self.usb_spec_version = 0x0001
21 self.device_class = device_class
22 self.device_subclass = device_subclass
23 self.protocol_rel_num = protocol_rel_num
24 self.max_packet_size_ep0 = max_packet_size_ep0
25 self.vendor_id = vendor_id
26 self.product_id = product_id
27 self.device_rev = device_rev
28 self.manufacturer_string_id = self.get_string_id(manufacturer_string)
29 self.product_string_id = self.get_string_id(product_string)
30 self.serial_number_string_id = self.get_string_id(serial_number_string)
32 # maps from USB.desc_type_* to bytearray OR callable
33 self.descriptors = descriptors
34 self.descriptors[USB.desc_type_device] = self.get_descriptor
35 self.descriptors[USB.desc_type_configuration] = self.handle_get_configuration_descriptor_request
36 self.descriptors[USB.desc_type_string] = self.handle_get_string_descriptor_request
39 self.configuration = None
40 self.configurations = configurations
42 for c in self.configurations:
43 csi = self.get_string_id(c.configuration_string)
44 c.set_configuration_string_index(csi)
46 for i in c.interfaces:
49 self.state = USB.state_detached
54 self.setup_request_handlers()
56 def get_string_id(self, s):
58 i = self.strings.index(s)
60 # string descriptors start at index 1
61 self.strings.append(s)
66 def setup_request_handlers(self):
67 # see table 9-4 of USB 2.0 spec, page 279
68 self.request_handlers = {
69 0 : self.handle_get_status_request,
70 1 : self.handle_clear_feature_request,
71 3 : self.handle_set_feature_request,
72 5 : self.handle_set_address_request,
73 6 : self.handle_get_descriptor_request,
74 7 : self.handle_set_descriptor_request,
75 8 : self.handle_get_configuration_request,
76 9 : self.handle_set_configuration_request,
77 10 : self.handle_get_interface_request,
78 11 : self.handle_set_interface_request,
79 12 : self.handle_synch_frame_request
83 self.maxusb_app.connect(self)
85 # skipping USB.state_attached may not be strictly correct (9.1.1.{1,2})
86 self.state = USB.state_powered
89 self.maxusb_app.disconnect()
91 self.state = USB.state_detached
94 self.maxusb_app.service_irqs()
96 def ack_status_stage(self):
97 self.maxusb_app.ack_status_stage()
99 def get_descriptor(self, n):
101 18, # length of descriptor in bytes
102 1, # descriptor type 1 == device
103 (self.usb_spec_version >> 8) & 0xff,
104 self.usb_spec_version & 0xff,
106 self.device_subclass,
107 self.protocol_rel_num,
108 self.max_packet_size_ep0,
109 (self.vendor_id >> 8) & 0xff,
110 self.vendor_id & 0xff,
111 (self.product_id >> 8) & 0xff,
112 self.product_id & 0xff,
113 (self.device_rev >> 8) & 0xff,
114 self.device_rev & 0xff,
115 self.manufacturer_string_id,
116 self.product_string_id,
117 self.serial_number_string_id,
118 len(self.configurations)
124 #####################################################
126 def handle_request(self, req):
128 print(self.name, "received request", req)
130 # figure out the intended recipient
131 if req.get_recipient() == USB.request_recipient_device:
133 elif req.get_recipient() == USB.request_recipient_interface:
134 recipient = self.configuration.interfaces[req.index]
135 elif req.get_recipient() == USB.request_recipient_endpoint:
137 recipient = self.endpoints[req.index]
139 self.maxusb_app.stall_ep0()
143 if req.get_type() == USB.request_type_standard:
144 handler = recipient.request_handlers[req.request]
146 elif req.get_type() == USB.request_type_class:
147 # HACK: evidently, FreeBSD doesn't pay attention to the device
148 # until it sends a GET_STATUS(class) message
150 self.maxusb_app.stall_ep0()
151 elif req.get_type() == USB.request_type_vendor:
152 self.maxusb_app.stall_ep0()
154 def handle_data_available(self, ep_num, data):
155 if self.state == USB.state_configured and ep_num in self.endpoints:
156 endpoint = self.endpoints[ep_num]
157 endpoint.handler(data)
159 def handle_buffer_available(self, ep_num):
160 if self.state == USB.state_configured and ep_num in self.endpoints:
161 endpoint = self.endpoints[ep_num]
164 # standard request handlers
165 #####################################################
167 # USB 2.0 specification, section 9.4.5 (p 282 of pdf)
168 def handle_get_status_request(self, req):
169 print(self.name, "received GET_STATUS request")
171 # self-powered and remote-wakeup (USB 2.0 Spec section 9.4.5)
172 response = b'\x03\x00'
173 self.maxusb_app.send_on_endpoint(0, response)
175 # USB 2.0 specification, section 9.4.1 (p 280 of pdf)
176 def handle_clear_feature_request(self, req):
177 print(self.name, "received CLEAR_FEATURE request with type 0x%02x and value 0x%02x" \
178 % (req.request_type, req.value))
180 # USB 2.0 specification, section 9.4.9 (p 286 of pdf)
181 def handle_set_feature_request(self, req):
182 print(self.name, "received SET_FEATURE request")
184 # USB 2.0 specification, section 9.4.6 (p 284 of pdf)
185 def handle_set_address_request(self, req):
186 self.address = req.value
187 self.state = USB.state_address
188 self.ack_status_stage()
191 print(self.name, "received SET_ADDRESS request for address",
194 # USB 2.0 specification, section 9.4.3 (p 281 of pdf)
195 def handle_get_descriptor_request(self, req):
196 dtype = (req.value >> 8) & 0xff
197 dindex = req.value & 0xff
204 print(self.name, ("received GET_DESCRIPTOR req %d, index %d, " \
205 + "language 0x%04x, length %d") \
206 % (dtype, dindex, lang, n))
208 # TODO: handle KeyError
209 response = self.descriptors[dtype]
210 if callable(response):
211 response = response(dindex)
214 n = min(n, len(response))
215 self.maxusb_app.verbose += 1
216 self.maxusb_app.send_on_endpoint(0, response[:n])
217 self.maxusb_app.verbose -= 1
220 print(self.name, "sent", n, "bytes in response")
222 def handle_get_configuration_descriptor_request(self, num):
223 return self.configurations[num].get_descriptor()
225 def handle_get_string_descriptor_request(self, num):
227 # HACK: hard-coding baaaaad
229 4, # length of descriptor in bytes
230 3, # descriptor type 3 == string
231 9, # language code 0, byte 0
232 4 # language code 0, byte 1
235 # string descriptors start at 1
236 s = self.strings[num-1].encode('utf-16')
238 # Linux doesn't like the leading 2-byte Byte Order Mark (BOM);
239 # FreeBSD is okay without it
243 len(s) + 2, # length of descriptor in bytes
244 3 # descriptor type 3 == string
250 # USB 2.0 specification, section 9.4.8 (p 285 of pdf)
251 def handle_set_descriptor_request(self, req):
252 print(self.name, "received SET_DESCRIPTOR request")
254 # USB 2.0 specification, section 9.4.2 (p 281 of pdf)
255 def handle_get_configuration_request(self, req):
256 print(self.name, "received GET_CONFIGURATION request with data 0x%02x" \
259 # USB 2.0 specification, section 9.4.7 (p 285 of pdf)
260 def handle_set_configuration_request(self, req):
261 print(self.name, "received SET_CONFIGURATION request")
263 # configs are one-based
264 self.config_num = req.value - 1
265 self.configuration = self.configurations[self.config_num]
266 self.state = USB.state_configured
268 # collate endpoint numbers
270 for i in self.configuration.interfaces:
271 for e in i.endpoints:
272 self.endpoints[e.number] = e
274 # HACK: blindly acknowledge request
275 self.ack_status_stage()
277 # USB 2.0 specification, section 9.4.4 (p 282 of pdf)
278 def handle_get_interface_request(self, req):
279 print(self.name, "received GET_INTERFACE request")
282 # HACK: currently only support one interface
283 self.maxusb_app.send_on_endpoint(0, b'\x00')
285 self.maxusb_app.stall_ep0()
287 # USB 2.0 specification, section 9.4.10 (p 288 of pdf)
288 def handle_set_interface_request(self, req):
289 print(self.name, "received SET_INTERFACE request")
291 # USB 2.0 specification, section 9.4.11 (p 288 of pdf)
292 def handle_synch_frame_request(self, req):
293 print(self.name, "received SYNCH_FRAME request")
296 class USBDeviceRequest:
297 def __init__(self, raw_bytes):
298 """Expects raw 8-byte setup data request packet"""
300 self.request_type = raw_bytes[0]
301 self.request = raw_bytes[1]
302 self.value = (raw_bytes[3] << 8) | raw_bytes[2]
303 self.index = (raw_bytes[5] << 8) | raw_bytes[4]
304 self.length = (raw_bytes[7] << 8) | raw_bytes[6]
307 s = "dir=%d, type=%d, rec=%d, r=%d, v=%d, i=%d, l=%d" \
308 % (self.get_direction(), self.get_type(), self.get_recipient(),
309 self.request, self.value, self.index, self.length)
313 """returns request as bytes"""
314 b = bytes([ self.request_type, self.request,
315 (self.value >> 8) & 0xff, self.value & 0xff,
316 (self.index >> 8) & 0xff, self.index & 0xff,
317 (self.length >> 8) & 0xff, self.length & 0xff
321 def get_direction(self):
322 return (self.request_type >> 7) & 0x01
325 return (self.request_type >> 5) & 0x03
327 def get_recipient(self):
328 return self.request_type & 0x1f