Rev 5363 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4850 | mario79 | 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
2 | ;; ;; |
||
10051 | ace_dent | 3 | ;; Copyright (C) KolibriOS team 2013-2024. All rights reserved. ;; |
4850 | mario79 | 4 | ;; Distributed under terms of the GNU General Public License ;; |
5 | ;; ;; |
||
6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
||
7 | |||
8 | |||
3520 | clevermous | 9 | ; Implementation of the USB protocol for device enumeration. |
10 | ; Manage a USB device when it becomes ready for USB commands: |
||
11 | ; configure, enumerate, load the corresponding driver(s), |
||
12 | ; pass device information to the driver. |
||
13 | |||
14 | ; ============================================================================= |
||
15 | ; ================================= Constants ================================= |
||
16 | ; ============================================================================= |
||
17 | ; USB standard request codes |
||
18 | USB_GET_STATUS = 0 |
||
19 | USB_CLEAR_FEATURE = 1 |
||
20 | USB_SET_FEATURE = 3 |
||
21 | USB_SET_ADDRESS = 5 |
||
22 | USB_GET_DESCRIPTOR = 6 |
||
23 | USB_SET_DESCRIPTOR = 7 |
||
24 | USB_GET_CONFIGURATION = 8 |
||
25 | USB_SET_CONFIGURATION = 9 |
||
26 | USB_GET_INTERFACE = 10 |
||
27 | USB_SET_INTERFACE = 11 |
||
28 | USB_SYNCH_FRAME = 12 |
||
29 | |||
30 | ; USB standard descriptor types |
||
31 | USB_DEVICE_DESCR = 1 |
||
32 | USB_CONFIG_DESCR = 2 |
||
33 | USB_STRING_DESCR = 3 |
||
34 | USB_INTERFACE_DESCR = 4 |
||
35 | USB_ENDPOINT_DESCR = 5 |
||
36 | USB_DEVICE_QUALIFIER_DESCR = 6 |
||
37 | USB_OTHER_SPEED_CONFIG_DESCR = 7 |
||
38 | USB_INTERFACE_POWER_DESCR = 8 |
||
39 | |||
40 | ; Compile-time setting. If set, the code will dump all descriptors as they are |
||
41 | ; read to the debug board. |
||
42 | USB_DUMP_DESCRIPTORS = 1 |
||
43 | |||
4547 | clevermous | 44 | ; According to the USB specification (9.2.6.3), |
45 | ; any device must response to SET_ADDRESS in 50 ms, or 5 timer ticks. |
||
46 | ; Of course, our world is far from ideal. |
||
47 | ; I have seen devices that just NAK everything when being reset from working |
||
48 | ; state, but start to work after second reset. |
||
49 | ; Our strategy is as follows: give 2 seconds for the first attempt, |
||
50 | ; this should be enough for normal devices and not too long to detect buggy ones. |
||
51 | ; If the device continues NAKing, reset it and retry several times, |
||
52 | ; doubling the interval: 2s -> 4s -> 8s -> 16s. Give up after that. |
||
53 | ; Numbers are quite arbitrary. |
||
54 | TIMEOUT_SET_ADDRESS_INITIAL = 200 |
||
55 | TIMEOUT_SET_ADDRESS_LAST = 1600 |
||
56 | |||
3520 | clevermous | 57 | ; ============================================================================= |
58 | ; ================================ Structures ================================= |
||
59 | ; ============================================================================= |
||
60 | ; USB descriptors. See USB specification for detailed explanations. |
||
61 | ; First two bytes of every descriptor have the same meaning. |
||
62 | struct usb_descr |
||
63 | bLength db ? |
||
64 | ; Size of this descriptor in bytes |
||
65 | bDescriptorType db ? |
||
66 | ; One of USB_*_DESCR constants. |
||
67 | ends |
||
68 | |||
69 | ; USB device descriptor |
||
70 | struct usb_device_descr usb_descr |
||
71 | bcdUSB dw ? |
||
72 | ; USB Specification Release number in BCD, e.g. 110h = USB 1.1 |
||
73 | bDeviceClass db ? |
||
74 | ; USB Device Class Code |
||
75 | bDeviceSubClass db ? |
||
76 | ; USB Device Subclass Code |
||
77 | bDeviceProtocol db ? |
||
78 | ; USB Device Protocol Code |
||
79 | bMaxPacketSize0 db ? |
||
80 | ; Maximum packet size for zero endpoint |
||
81 | idVendor dw ? |
||
82 | ; Vendor ID |
||
83 | idProduct dw ? |
||
84 | ; Product ID |
||
85 | bcdDevice dw ? |
||
86 | ; Device release number in BCD |
||
87 | iManufacturer db ? |
||
88 | ; Index of string descriptor describing manufacturer |
||
89 | iProduct db ? |
||
90 | ; Index of string descriptor describing product |
||
91 | iSerialNumber db ? |
||
92 | ; Index of string descriptor describing serial number |
||
93 | bNumConfigurations db ? |
||
94 | ; Number of possible configurations |
||
95 | ends |
||
96 | |||
97 | ; USB configuration descriptor |
||
98 | struct usb_config_descr usb_descr |
||
99 | wTotalLength dw ? |
||
100 | ; Total length of data returned for this configuration |
||
101 | bNumInterfaces db ? |
||
102 | ; Number of interfaces in this configuration |
||
103 | bConfigurationValue db ? |
||
104 | ; Value for SET_CONFIGURATION control request |
||
105 | iConfiguration db ? |
||
106 | ; Index of string descriptor describing this configuration |
||
107 | bmAttributes db ? |
||
108 | ; Bit 6 is SelfPowered, bit 5 is RemoteWakeupSupported, |
||
109 | ; bit 7 must be 1, other bits must be 0 |
||
110 | bMaxPower db ? |
||
111 | ; Maximum power consumption from the bus in 2mA units |
||
112 | ends |
||
113 | |||
114 | ; USB interface descriptor |
||
115 | struct usb_interface_descr usb_descr |
||
116 | ; The following two fields work in pair. Sometimes one interface can work |
||
117 | ; in different modes; e.g. videostream from web-cameras requires different |
||
118 | ; bandwidth depending on resolution/quality/compression settings. |
||
119 | ; Each mode of each interface has its own descriptor with its own endpoints |
||
120 | ; following; all descriptors for one interface have the same bInterfaceNumber, |
||
121 | ; and different bAlternateSetting. |
||
122 | ; By default, any interface operates in mode with bAlternateSetting = 0. |
||
123 | ; Often this is the only mode. If there are another modes, the active mode |
||
124 | ; is selected by SET_INTERFACE(bAlternateSetting) control request. |
||
125 | bInterfaceNumber db ? |
||
126 | bAlternateSetting db ? |
||
127 | bNumEndpoints db ? |
||
128 | ; Number of endpoints used by this interface, excluding zero endpoint |
||
129 | bInterfaceClass db ? |
||
130 | ; USB Interface Class Code |
||
131 | bInterfaceSubClass db ? |
||
132 | ; USB Interface Subclass Code |
||
133 | bInterfaceProtocol db ? |
||
134 | ; USB Interface Protocol Code |
||
135 | iInterface db ? |
||
136 | ; Index of string descriptor describing this interface |
||
137 | ends |
||
138 | |||
139 | ; USB endpoint descriptor |
||
140 | struct usb_endpoint_descr usb_descr |
||
141 | bEndpointAddress db ? |
||
142 | ; Lower 4 bits form endpoint number, |
||
143 | ; upper bit is 0 for OUT endpoints and 1 for IN endpoints, |
||
144 | ; other bits must be zero |
||
145 | bmAttributes db ? |
||
146 | ; Lower 2 bits form transfer type, one of *_PIPE, |
||
147 | ; other bits must be zero for non-isochronous endpoints; |
||
148 | ; refer to the USB specification for meaning in isochronous case |
||
149 | wMaxPacketSize dw ? |
||
150 | ; Lower 11 bits form maximum packet size, |
||
151 | ; next two bits specify the number of additional transactions per microframe |
||
152 | ; for high-speed periodic endpoints, other bits must be zero. |
||
153 | bInterval db ? |
||
154 | ; Interval for polling endpoint for data transfers. |
||
155 | ; Isochronous and high-speed interrupt endpoints: poll every 2^(bInterval-1) |
||
156 | ; (micro)frames |
||
157 | ; Full/low-speed interrupt endpoints: poll every bInterval frames |
||
158 | ; High-speed bulk/control OUT endpoints: maximum NAK rate |
||
159 | ends |
||
160 | |||
161 | ; ============================================================================= |
||
162 | ; =================================== Code ==================================== |
||
163 | ; ============================================================================= |
||
164 | |||
165 | ; When a new device is ready to be configured, a controller-specific code |
||
166 | ; calls usb_new_device. |
||
167 | ; The sequence of further actions: |
||
168 | ; * open pipe for the zero endpoint (usb_new_device); |
||
169 | ; maximum packet size is not known yet, but it must be at least 8 bytes, |
||
170 | ; so it is safe to send packets with <= 8 bytes |
||
171 | ; * issue SET_ADDRESS control request (usb_new_device) |
||
172 | ; * set the new device address in the pipe (usb_set_address_callback) |
||
173 | ; * notify a controller-specific code that initialization of other ports |
||
174 | ; can be started (usb_set_address_callback) |
||
175 | ; * issue GET_DESCRIPTOR control request for first 8 bytes of device descriptor |
||
176 | ; (usb_after_set_address) |
||
177 | ; * first 8 bytes of device descriptor contain the true packet size for zero |
||
178 | ; endpoint, so set the true packet size (usb_get_descr8_callback) |
||
179 | ; * first 8 bytes of a descriptor contain the full size of this descriptor, |
||
180 | ; issue GET_DESCRIPTOR control request for the full device descriptor |
||
181 | ; (usb_after_set_endpoint_size) |
||
182 | ; * issue GET_DESCRIPTOR control request for first 8 bytes of configuration |
||
183 | ; descriptor (usb_get_descr_callback) |
||
184 | ; * issue GET_DESCRIPTOR control request for full configuration descriptor |
||
185 | ; (usb_know_length_callback) |
||
186 | ; * issue SET_CONFIGURATION control request (usb_set_config_callback) |
||
187 | ; * parse configuration descriptor, load the corresponding driver(s), |
||
188 | ; pass the configuration descriptor to the driver and let the driver do |
||
189 | ; the further work (usb_got_config_callback) |
||
190 | |||
191 | ; This function is called from controller-specific part |
||
192 | ; when a new device is ready to be configured. |
||
193 | ; in: ecx -> pseudo-pipe, part of usb_pipe |
||
194 | ; in: esi -> usb_controller |
||
195 | ; in: [esi+usb_controller.ResettingHub] is the pointer to usb_hub for device, |
||
196 | ; NULL if the device is connected to the root hub |
||
197 | ; in: [esi+usb_controller.ResettingPort] is the port for the device, zero-based |
||
198 | ; in: [esi+usb_controller.ResettingSpeed] is the speed of the device, one of |
||
199 | ; USB_SPEED_xx. |
||
200 | ; out: eax = 0 <=> failed, the caller should disable the port. |
||
201 | proc usb_new_device |
||
202 | push ebx edi ; save used registers to be stdcall |
||
4547 | clevermous | 203 | ; 1. Check whether we're here because we were trying to reset |
204 | ; already-registered device in hope to fix something serious. |
||
205 | ; If so, skip allocation and go to 6. |
||
206 | movzx eax, [esi+usb_controller.ResettingPort] |
||
207 | mov edx, [esi+usb_controller.ResettingHub] |
||
208 | test edx, edx |
||
209 | jz .test_roothub |
||
210 | mov edx, [edx+usb_hub.ConnectedDevicesPtr] |
||
211 | mov ebx, [edx+eax*4] |
||
212 | jmp @f |
||
213 | .test_roothub: |
||
214 | mov ebx, [esi+usb_controller.DevicesByPort+eax*4] |
||
215 | @@: |
||
216 | test ebx, ebx |
||
217 | jnz .try_set_address |
||
218 | ; 2. Allocate resources. Any device uses the following resources: |
||
3520 | clevermous | 219 | ; - device address in the bus |
220 | ; - memory for device data |
||
221 | ; - pipe for zero endpoint |
||
222 | ; If some allocation fails, we must undo our actions. Closing the pipe |
||
223 | ; is a hard task, so we avoid it and open the pipe as the last resource. |
||
224 | ; The order for other two allocations is quite arbitrary. |
||
4547 | clevermous | 225 | ; 2a. Allocate a bus address. |
3520 | clevermous | 226 | push ecx |
227 | call usb_set_address_request |
||
228 | pop ecx |
||
4547 | clevermous | 229 | ; 2b. If failed, just return zero. |
3520 | clevermous | 230 | test eax, eax |
231 | jz .nothing |
||
4547 | clevermous | 232 | ; 2c. Allocate memory for device data. |
3520 | clevermous | 233 | ; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR |
234 | ; input and output, see usb_after_set_address. Later we will reallocate it |
||
235 | ; to actual size needed for descriptors. |
||
4138 | clevermous | 236 | movi eax, sizeof.usb_device_data + 8 |
3520 | clevermous | 237 | push ecx |
238 | call malloc |
||
239 | pop ecx |
||
4547 | clevermous | 240 | ; 2d. If failed, free the bus address and return zero. |
3520 | clevermous | 241 | test eax, eax |
242 | jz .nomemory |
||
4547 | clevermous | 243 | ; 2e. Open pipe for endpoint zero. |
3520 | clevermous | 244 | ; For now, we do not know the actual maximum packet size; |
245 | ; for full-speed devices it can be any of 8, 16, 32, 64 bytes, |
||
246 | ; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes. |
||
247 | ; Thus, we must use some fake "maximum packet size" until the actual size |
||
248 | ; will be known. However, the maximum packet size must be at least 8, and |
||
249 | ; initial stages of the configuration process involves only packets of <= 8 |
||
250 | ; bytes, they will be transferred correctly as long as |
||
251 | ; the fake "maximum packet size" is also at least 8. |
||
252 | ; Thus, any number >= 8 is suitable for actual hardware. |
||
253 | ; However, software emulation of EHCI in VirtualBox assumes that high-speed |
||
254 | ; control transfers are those originating from pipes with max packet size = 64, |
||
255 | ; even on early stages of the configuration process. This is incorrect, |
||
256 | ; but we have no specific preferences, so let VirtualBox be happy and use 64 |
||
257 | ; as the fake "maximum packet size". |
||
258 | push eax |
||
259 | ; We will need many zeroes. |
||
260 | ; "push edi" is one byte, "push 0" is two bytes; save space, use edi. |
||
261 | xor edi, edi |
||
262 | stdcall usb_open_pipe, ecx, edi, 64, edi, edi |
||
263 | ; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes. |
||
264 | xchg eax, ebx |
||
265 | pop eax |
||
4547 | clevermous | 266 | ; 2f. If failed, free the memory, the bus address and return zero. |
3520 | clevermous | 267 | test ebx, ebx |
268 | jz .freememory |
||
4547 | clevermous | 269 | ; 3. Store pointer to device data in the pipe structure. |
3520 | clevermous | 270 | mov [ebx+usb_pipe.DeviceData], eax |
4547 | clevermous | 271 | ; 4. Init device data, using usb_controller.Resetting* variables. |
272 | mov [eax+usb_device_data.Timer], edi |
||
273 | mov dword [eax+usb_device_data.DeviceDescriptor], TIMEOUT_SET_ADDRESS_INITIAL |
||
3826 | clevermous | 274 | mov [eax+usb_device_data.TTHub], edi |
275 | mov [eax+usb_device_data.TTPort], 0 |
||
276 | mov [eax+usb_device_data.NumInterfaces], edi |
||
277 | mov [eax+usb_device_data.DeviceDescrSize], 0 |
||
278 | mov dl, [esi+usb_controller.ResettingSpeed] |
||
279 | mov [eax+usb_device_data.Speed], dl |
||
3520 | clevermous | 280 | mov [eax+usb_device_data.NumPipes], 1 |
3826 | clevermous | 281 | push ebx |
282 | cmp dl, USB_SPEED_HS |
||
283 | jz .nott |
||
284 | mov ebx, [esi+usb_controller.ResettingHub] |
||
285 | test ebx, ebx |
||
286 | jz .nott |
||
287 | mov cl, [esi+usb_controller.ResettingPort] |
||
288 | mov edx, [ebx+usb_hub.ConfigPipe] |
||
289 | mov edx, [edx+usb_pipe.DeviceData] |
||
290 | cmp [edx+usb_device_data.TTHub], 0 |
||
291 | jz @f |
||
292 | mov cl, [edx+usb_device_data.TTPort] |
||
293 | mov ebx, [edx+usb_device_data.TTHub] |
||
294 | jmp .has_tt |
||
295 | @@: |
||
296 | cmp [edx+usb_device_data.Speed], USB_SPEED_HS |
||
297 | jnz .nott |
||
298 | .has_tt: |
||
299 | mov [eax+usb_device_data.TTHub], ebx |
||
300 | mov [eax+usb_device_data.TTPort], cl |
||
301 | .nott: |
||
302 | pop ebx |
||
3520 | clevermous | 303 | mov [eax+usb_device_data.ConfigDataSize], edi |
304 | mov [eax+usb_device_data.Interfaces], edi |
||
305 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
3826 | clevermous | 306 | mov [eax+usb_device_data.Port], cl |
3520 | clevermous | 307 | mov edx, [esi+usb_controller.ResettingHub] |
308 | mov [eax+usb_device_data.Hub], edx |
||
4547 | clevermous | 309 | ; 5. Store pointer to the config pipe in the hub data. |
3520 | clevermous | 310 | ; Config pipe serves as device identifier. |
311 | ; Root hubs use the array inside usb_controller structure, |
||
312 | ; non-root hubs use the array immediately after usb_hub structure. |
||
313 | test edx, edx |
||
314 | jz .roothub |
||
315 | mov edx, [edx+usb_hub.ConnectedDevicesPtr] |
||
316 | mov [edx+ecx*4], ebx |
||
317 | jmp @f |
||
318 | .roothub: |
||
319 | mov [esi+usb_controller.DevicesByPort+ecx*4], ebx |
||
320 | @@: |
||
321 | call usb_reinit_pipe_list |
||
4547 | clevermous | 322 | ; 6. Issue SET_ADDRESS control request, using buffer filled in step 2a. |
323 | ; 6a. Configure timer to force reset after timeout. |
||
324 | ; Note: we can't use self-destructing timer, because we need to be able to cancel it, |
||
325 | ; and for self-destructing timer we could have race condition in cancelling/destructing. |
||
326 | ; DEBUGF 1,'K : pipe %x\n',ebx |
||
327 | .try_set_address: |
||
328 | xor edi, edi |
||
329 | mov edx, [ebx+usb_pipe.DeviceData] |
||
330 | stdcall timer_hs, [edx+usb_device_data.DeviceDescriptor], 7FFFFFFFh, usb_abort_pipe, ebx |
||
331 | test eax, eax |
||
332 | jz .nothing |
||
333 | mov edx, [ebx+usb_pipe.DeviceData] |
||
334 | mov [edx+usb_device_data.Timer], eax |
||
335 | ; 6b. If it succeeded, setup timer to configure wait timeout. |
||
336 | lea eax, [esi+usb_controller.SetAddressBuffer] |
||
337 | stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi |
||
3520 | clevermous | 338 | ; Use the return value from usb_control_async as our return value; |
339 | ; if it is zero, then something has failed. |
||
340 | .nothing: |
||
4547 | clevermous | 341 | ; 7. Return. |
3520 | clevermous | 342 | pop edi ebx ; restore used registers to be stdcall |
343 | ret |
||
4547 | clevermous | 344 | ; Handlers of failures in steps 2b, 2d, 2f. |
3520 | clevermous | 345 | .freememory: |
346 | call free |
||
347 | jmp .freeaddr |
||
348 | .nomemory: |
||
349 | dbgstr 'No memory for device data' |
||
350 | .freeaddr: |
||
351 | mov ecx, dword [esi+usb_controller.SetAddressBuffer+2] |
||
352 | bts [esi+usb_controller.ExistingAddresses], ecx |
||
353 | xor eax, eax |
||
354 | jmp .nothing |
||
355 | endp |
||
356 | |||
357 | ; Helper procedure for usb_new_device. |
||
358 | ; Allocates a new USB address and fills usb_controller.SetAddressBuffer |
||
359 | ; with data for SET_ADDRESS(allocated_address) request. |
||
360 | ; out: eax = 0 <=> failed |
||
361 | ; Destroys edi. |
||
362 | proc usb_set_address_request |
||
363 | ; There are 128 bits, one for each possible address. |
||
364 | ; Note: only the USB thread works with usb_controller.ExistingAddresses, |
||
365 | ; so there is no need for synchronization. |
||
366 | ; We must find a bit set to 1 and clear it. |
||
367 | ; 1. Find the first dword which has a nonzero bit = which is nonzero. |
||
368 | mov ecx, 128/32 |
||
369 | lea edi, [esi+usb_controller.ExistingAddresses] |
||
370 | xor eax, eax |
||
371 | repz scasd |
||
372 | ; 2. If all dwords are zero, return an error. |
||
373 | jz .error |
||
374 | ; 3. The dword at [edi-4] is nonzero. Find the lowest nonzero bit. |
||
375 | bsf eax, [edi-4] |
||
376 | ; Now eax = bit number inside the dword at [edi-4]. |
||
377 | ; 4. Clear the bit. |
||
378 | btr [edi-4], eax |
||
379 | ; 5. Generate the address by edi = memory address and eax = bit inside dword. |
||
380 | ; Address = eax + 8 * (edi-4 - (esi+usb_controller.ExistingAddress)). |
||
381 | sub edi, esi |
||
382 | lea edi, [eax+(edi-4-usb_controller.ExistingAddresses)*8] |
||
383 | ; 6. Store the allocated address in SetAddressBuffer and fill remaining fields. |
||
384 | ; Note that usb_controller is zeroed at allocation, so only command byte needs |
||
385 | ; to be filled. |
||
386 | mov byte [esi+usb_controller.SetAddressBuffer+1], USB_SET_ADDRESS |
||
387 | mov dword [esi+usb_controller.SetAddressBuffer+2], edi |
||
388 | ; 7. Return non-zero value in eax. |
||
389 | inc eax |
||
390 | .nothing: |
||
391 | ret |
||
392 | .error: |
||
393 | dbgstr 'cannot allocate USB address' |
||
394 | xor eax, eax |
||
395 | jmp .nothing |
||
396 | endp |
||
397 | |||
398 | ; This procedure is called by USB stack when SET_ADDRESS request initiated by |
||
399 | ; usb_new_device is completed, either successfully or unsuccessfully. |
||
400 | ; Note that USB stack uses esi = pointer to usb_controller. |
||
401 | proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
||
402 | push ebx ; save ebx to be stdcall |
||
4547 | clevermous | 403 | mov ebx, [pipe] |
404 | ; 1. In any case, cancel the timer. |
||
405 | mov eax, [ebx+usb_pipe.DeviceData] |
||
406 | stdcall cancel_timer_hs, [eax+usb_device_data.Timer] |
||
407 | mov eax, [ebx+usb_pipe.DeviceData] |
||
408 | mov [eax+usb_device_data.Timer], 0 |
||
3520 | clevermous | 409 | ; Load data to registers for further references. |
410 | mov ecx, dword [esi+usb_controller.SetAddressBuffer+2] |
||
411 | mov eax, [esi+usb_controller.HardwareFunc] |
||
4547 | clevermous | 412 | ; 2. Check whether the device has accepted new address. If so, proceed to 3. |
413 | ; Otherwise, go to 4 if killed by usb_set_address_timeout or to 5 otherwise. |
||
414 | cmp [status], USB_STATUS_CANCELLED |
||
415 | jz .timeout |
||
3520 | clevermous | 416 | cmp [status], 0 |
417 | jnz .error |
||
4547 | clevermous | 418 | ; 3. Address accepted. |
419 | ; 3a. The controller-specific structure for the control pipe still uses |
||
3520 | clevermous | 420 | ; zero address. Call the controller-specific function to change it to |
421 | ; the actual address. |
||
422 | ; Note that the hardware could cache the controller-specific structure, |
||
423 | ; so setting the address could take some time until the cache is evicted. |
||
424 | ; Thus, the call is asynchronous; meet us in usb_after_set_address when it will |
||
425 | ; be safe to continue. |
||
4418 | clevermous | 426 | ; dbgstr 'address set in device' |
3520 | clevermous | 427 | call [eax+usb_hardware_func.SetDeviceAddress] |
4547 | clevermous | 428 | ; 3b. If the port is in non-root hub, clear 'reset in progress' flag. |
429 | ; In any case, proceed to 6. |
||
3520 | clevermous | 430 | mov eax, [esi+usb_controller.ResettingHub] |
431 | test eax, eax |
||
432 | jz .return |
||
433 | and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS |
||
434 | .return: |
||
4547 | clevermous | 435 | ; 6. Address configuration done, we can proceed with other ports. |
3520 | clevermous | 436 | ; Call the worker function for that. |
437 | call usb_test_pending_port |
||
4547 | clevermous | 438 | .wakeup: |
439 | push esi edi |
||
440 | call usb_wakeup |
||
441 | pop edi esi |
||
3520 | clevermous | 442 | .nothing: |
443 | pop ebx ; restore ebx to be stdcall |
||
444 | ret |
||
4547 | clevermous | 445 | .timeout: |
446 | ; 4. Device continues to NAK the request. Reset it and retry. |
||
447 | mov edx, [ebx+usb_pipe.DeviceData] |
||
448 | mov ecx, [edx+usb_device_data.DeviceDescriptor] |
||
449 | add ecx, ecx |
||
450 | cmp ecx, TIMEOUT_SET_ADDRESS_LAST |
||
451 | ja .error |
||
452 | mov [edx+usb_device_data.DeviceDescriptor], ecx |
||
453 | dbgstr 'Timeout in USB device initialization, trying to reset...' |
||
454 | cmp [esi+usb_controller.ResettingHub], 0 |
||
455 | jz .reset_roothub |
||
456 | push esi |
||
457 | mov esi, [esi+usb_controller.ResettingHub] |
||
458 | call usb_hub_initiate_reset |
||
459 | pop esi |
||
460 | jmp .nothing |
||
461 | .reset_roothub: |
||
462 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
463 | call [eax+usb_hardware_func.InitiateReset] |
||
464 | jmp .wakeup |
||
3520 | clevermous | 465 | .error: |
4547 | clevermous | 466 | ; 5. Device error: device not responding, disconnect etc. |
3520 | clevermous | 467 | DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status] |
4547 | clevermous | 468 | ; 5a. The address has not been accepted. Mark it as free. |
3520 | clevermous | 469 | bts dword [esi+usb_controller.ExistingAddresses], ecx |
4547 | clevermous | 470 | ; 5b. Disable the port with bad device. |
3520 | clevermous | 471 | ; For the root hub, call the controller-specific function and go to 6. |
472 | ; For non-root hubs, let the hub code do its work and return (the request |
||
473 | ; could take some time, the hub code is responsible for proceeding). |
||
474 | cmp [esi+usb_controller.ResettingHub], 0 |
||
475 | jz .roothub |
||
476 | mov eax, [esi+usb_controller.ResettingHub] |
||
477 | call usb_hub_disable_resetting_port |
||
478 | jmp .nothing |
||
479 | .roothub: |
||
480 | movzx ecx, [esi+usb_controller.ResettingPort] |
||
481 | call [eax+usb_hardware_func.PortDisable] |
||
482 | jmp .return |
||
483 | endp |
||
484 | |||
485 | ; This procedure is called from usb_subscription_done when the hardware cache |
||
486 | ; is cleared after request from usb_set_address_callback. |
||
487 | ; in: ebx -> usb_pipe |
||
488 | proc usb_after_set_address |
||
4418 | clevermous | 489 | ; dbgstr 'address set for controller' |
3520 | clevermous | 490 | ; Issue control transfer GET_DESCRIPTOR(DEVICE_DESCR) for first 8 bytes. |
491 | ; Remember, we still do not know the actual packet size; |
||
492 | ; 8-bytes-request is safe. |
||
493 | ; usb_new_device has allocated 8 extra bytes besides sizeof.usb_device_data; |
||
494 | ; use them for both input and output. |
||
495 | mov eax, [ebx+usb_pipe.DeviceData] |
||
496 | add eax, usb_device_data.DeviceDescriptor |
||
497 | mov dword [eax], \ |
||
498 | 80h + \ ; device-to-host, standard, device-wide |
||
499 | (USB_GET_DESCRIPTOR shl 8) + \ ; request |
||
500 | (0 shl 16) + \ ; descriptor index: there is only one |
||
501 | (USB_DEVICE_DESCR shl 24) ; descriptor type |
||
502 | mov dword [eax+4], 8 shl 16 ; data length |
||
503 | stdcall usb_control_async, ebx, eax, eax, 8, usb_get_descr8_callback, eax, 0 |
||
504 | ret |
||
505 | endp |
||
506 | |||
507 | ; This procedure is called by USB stack when GET_DESCRIPTOR(DEVICE_DESCR) |
||
508 | ; request initiated by usb_after_set_address is completed, either successfully |
||
509 | ; or unsuccessfully. |
||
510 | ; Note that USB stack uses esi = pointer to usb_controller. |
||
511 | proc usb_get_descr8_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
||
512 | ; mov eax, [buffer] |
||
513 | ; DEBUGF 1,'K : descr8: l=%x; %x %x %x %x %x %x %x %x\n',[length],\ |
||
514 | ; [eax]:2,[eax+1]:2,[eax+2]:2,[eax+3]:2,[eax+4]:2,[eax+5]:2,[eax+6]:2,[eax+7]:2 |
||
515 | push edi ebx ; save used registers to be stdcall |
||
516 | mov ebx, [pipe] |
||
517 | ; 1. Check whether the operation was successful. |
||
518 | ; If not, say something to the debug board and stop the initialization. |
||
519 | cmp [status], 0 |
||
520 | jnz .error |
||
521 | ; 2. Length of descriptor must be at least sizeof.usb_device_descr bytes. |
||
522 | ; If not, say something to the debug board and stop the initialization. |
||
523 | mov eax, [ebx+usb_pipe.DeviceData] |
||
524 | cmp [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bLength], sizeof.usb_device_descr |
||
525 | jb .error |
||
526 | ; 3. Now first 8 bytes of device descriptor are known; |
||
527 | ; set DeviceDescrSize accordingly. |
||
528 | mov [eax+usb_device_data.DeviceDescrSize], 8 |
||
529 | ; 4. The controller-specific structure for the control pipe still uses |
||
530 | ; the fake "maximum packet size". Call the controller-specific function to |
||
531 | ; change it to the actual packet size from the device. |
||
532 | ; Note that the hardware could cache the controller-specific structure, |
||
533 | ; so changing it could take some time until the cache is evicted. |
||
534 | ; Thus, the call is asynchronous; meet us in usb_after_set_endpoint_size |
||
535 | ; when it will be safe to continue. |
||
536 | movzx ecx, [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bMaxPacketSize0] |
||
537 | mov eax, [esi+usb_controller.HardwareFunc] |
||
538 | call [eax+usb_hardware_func.SetEndpointPacketSize] |
||
539 | .nothing: |
||
540 | ; 5. Return. |
||
541 | pop ebx edi ; restore used registers to be stdcall |
||
542 | ret |
||
543 | .error: |
||
544 | dbgstr 'error with USB device descriptor' |
||
545 | jmp .nothing |
||
546 | endp |
||
547 | |||
548 | ; This procedure is called from usb_subscription_done when the hardware cache |
||
549 | ; is cleared after request from usb_get_descr8_callback. |
||
550 | ; in: ebx -> usb_pipe |
||
551 | proc usb_after_set_endpoint_size |
||
552 | ; 1. Reallocate memory for device data: |
||
553 | ; add memory for now-known size of device descriptor and extra 8 bytes |
||
554 | ; for further actions. |
||
555 | ; 1a. Allocate new memory. |
||
556 | mov eax, [ebx+usb_pipe.DeviceData] |
||
557 | movzx eax, [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bLength] |
||
558 | ; save length for step 2 |
||
559 | push eax |
||
560 | add eax, sizeof.usb_device_data + 8 |
||
561 | call malloc |
||
562 | ; 1b. If failed, say something to the debug board and stop the initialization. |
||
563 | test eax, eax |
||
564 | jz .nomemory |
||
565 | ; 1c. Copy data from old memory to new memory and switch the pointer in usb_pipe. |
||
566 | push eax |
||
567 | push esi edi |
||
568 | mov esi, [ebx+usb_pipe.DeviceData] |
||
569 | mov [ebx+usb_pipe.DeviceData], eax |
||
570 | mov edi, eax |
||
571 | mov eax, esi |
||
3826 | clevermous | 572 | mov ecx, sizeof.usb_device_data / 4 |
573 | rep movsd |
||
3520 | clevermous | 574 | pop edi esi |
575 | call usb_reinit_pipe_list |
||
576 | ; 1d. Free the old memory. |
||
577 | call free |
||
578 | pop eax |
||
579 | ; 2. Issue control transfer GET_DESCRIPTOR(DEVICE) for full descriptor. |
||
580 | ; restore length saved in step 1a |
||
581 | pop edx |
||
582 | add eax, sizeof.usb_device_data |
||
583 | mov dword [eax], \ |
||
584 | 80h + \ ; device-to-host, standard, device-wide |
||
585 | (USB_GET_DESCRIPTOR shl 8) + \ ; request |
||
586 | (0 shl 16) + \ ; descriptor index: there is only one |
||
587 | (USB_DEVICE_DESCR shl 24) ; descriptor type |
||
588 | and dword [eax+4], 0 |
||
589 | mov [eax+6], dl ; data length |
||
590 | stdcall usb_control_async, ebx, eax, eax, edx, usb_get_descr_callback, eax, 0 |
||
591 | ; 3. Return. |
||
592 | ret |
||
593 | .nomemory: |
||
594 | dbgstr 'No memory for device data' |
||
595 | ret |
||
596 | endp |
||
597 | |||
598 | ; This procedure is called by USB stack when GET_DESCRIPTOR(DEVICE) |
||
599 | ; request initiated by usb_after_set_endpoint_size is completed, |
||
600 | ; either successfully or unsuccessfully. |
||
601 | proc usb_get_descr_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
||
602 | ; Note: the prolog is the same as in usb_get_descr8_callback. |
||
603 | push edi ebx ; save used registers to be stdcall |
||
604 | ; 1. Check whether the operation was successful. |
||
605 | ; If not, say something to the debug board and stop the initialization. |
||
606 | cmp [status], 0 |
||
607 | jnz usb_get_descr8_callback.error |
||
608 | ; The full descriptor is known, dump it if specified by compile-time option. |
||
609 | if USB_DUMP_DESCRIPTORS |
||
610 | mov eax, [buffer] |
||
611 | mov ecx, [length] |
||
612 | sub ecx, 8 |
||
613 | jbe .skipdebug |
||
614 | DEBUGF 1,'K : device descriptor:' |
||
615 | @@: |
||
616 | DEBUGF 1,' %x',[eax]:2 |
||
617 | inc eax |
||
618 | dec ecx |
||
619 | jnz @b |
||
620 | DEBUGF 1,'\n' |
||
621 | .skipdebug: |
||
622 | end if |
||
623 | ; 2. Check that bLength is the same as was in the previous request. |
||
624 | ; If not, say something to the debug board and stop the initialization. |
||
625 | ; It is important, because usb_after_set_endpoint_size has allocated memory |
||
626 | ; according to the old bLength. Note that [length] for control transfers |
||
627 | ; includes 8 bytes of setup packet, so data length = [length] - 8. |
||
628 | mov eax, [buffer] |
||
629 | movzx ecx, [eax+usb_device_descr.bLength] |
||
630 | add ecx, 8 |
||
631 | cmp [length], ecx |
||
632 | jnz usb_get_descr8_callback.error |
||
633 | ; Amuse the user if she is watching the debug board. |
||
634 | mov cl, [eax+usb_device_descr.bNumConfigurations] |
||
635 | DEBUGF 1,'K : found USB device with ID %x:%x, %d configuration(s)\n',\ |
||
636 | [eax+usb_device_descr.idVendor]:4,\ |
||
637 | [eax+usb_device_descr.idProduct]:4,\ |
||
638 | cl |
||
639 | ; 3. If there are no configurations, stop the initialization. |
||
640 | cmp [eax+usb_device_descr.bNumConfigurations], 0 |
||
641 | jz .nothing |
||
642 | ; 4. Copy length of device descriptor to device data structure. |
||
643 | movzx edx, [eax+usb_device_descr.bLength] |
||
644 | mov [eax+usb_device_data.DeviceDescrSize-usb_device_data.DeviceDescriptor], dl |
||
645 | ; 5. Issue control transfer GET_DESCRIPTOR(CONFIGURATION). We do not know |
||
646 | ; the full length of that descriptor, so start with first 8 bytes, they contain |
||
647 | ; the full length. |
||
648 | ; usb_after_set_endpoint_size has allocated 8 extra bytes after the |
||
649 | ; device descriptor, use them for both input and output. |
||
650 | add eax, edx |
||
651 | mov dword [eax], \ |
||
652 | 80h + \ ; device-to-host, standard, device-wide |
||
653 | (USB_GET_DESCRIPTOR shl 8) + \ ; request |
||
654 | (0 shl 16) + \ ; descriptor index: there is only one |
||
655 | (USB_CONFIG_DESCR shl 24) ; descriptor type |
||
656 | mov dword [eax+4], 8 shl 16 ; data length |
||
657 | stdcall usb_control_async, [pipe], eax, eax, 8, usb_know_length_callback, eax, 0 |
||
658 | .nothing: |
||
659 | ; 6. Return. |
||
660 | pop ebx edi ; restore used registers to be stdcall |
||
661 | ret |
||
662 | endp |
||
663 | |||
664 | ; This procedure is called by USB stack when GET_DESCRIPTOR(CONFIGURATION) |
||
665 | ; request initiated by usb_get_descr_callback is completed, |
||
666 | ; either successfully or unsuccessfully. |
||
667 | proc usb_know_length_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
||
668 | push ebx ; save used registers to be stdcall |
||
669 | ; 1. Check whether the operation was successful. |
||
670 | ; If not, say something to the debug board and stop the initialization. |
||
671 | cmp [status], 0 |
||
672 | jnz .error |
||
673 | ; 2. Get the total length of data associated with config descriptor and store |
||
674 | ; it in device data structure. The total length must be at least |
||
675 | ; sizeof.usb_config_descr bytes; if not, say something to the debug board and |
||
676 | ; stop the initialization. |
||
677 | mov eax, [buffer] |
||
678 | mov edx, [pipe] |
||
679 | movzx ecx, [eax+usb_config_descr.wTotalLength] |
||
680 | mov eax, [edx+usb_pipe.DeviceData] |
||
681 | cmp ecx, sizeof.usb_config_descr |
||
682 | jb .error |
||
683 | mov [eax+usb_device_data.ConfigDataSize], ecx |
||
684 | ; 3. Reallocate memory for device data: |
||
685 | ; include usb_device_data structure, device descriptor, |
||
686 | ; config descriptor with all associated data, and extra bytes |
||
687 | ; sufficient for 8 bytes control packet and for one usb_interface_data struc. |
||
688 | ; Align extra bytes to dword boundary. |
||
689 | if sizeof.usb_interface_data > 8 |
||
690 | .extra_size = sizeof.usb_interface_data |
||
691 | else |
||
692 | .extra_size = 8 |
||
693 | end if |
||
694 | ; 3a. Allocate new memory. |
||
695 | movzx edx, [eax+usb_device_data.DeviceDescrSize] |
||
696 | lea eax, [ecx+edx+sizeof.usb_device_data+.extra_size+3] |
||
697 | and eax, not 3 |
||
698 | push eax |
||
699 | call malloc |
||
700 | pop edx |
||
701 | ; 3b. If failed, say something to the debug board and stop the initialization. |
||
702 | test eax, eax |
||
703 | jz .nomemory |
||
704 | ; 3c. Copy data from old memory to new memory and switch the pointer in usb_pipe. |
||
705 | push eax |
||
706 | mov ebx, [pipe] |
||
707 | push esi edi |
||
708 | mov esi, [ebx+usb_pipe.DeviceData] |
||
709 | mov edi, eax |
||
710 | mov [ebx+usb_pipe.DeviceData], eax |
||
711 | mov eax, esi |
||
712 | movzx ecx, [esi+usb_device_data.DeviceDescrSize] |
||
713 | sub edx, .extra_size |
||
714 | mov [esi+usb_device_data.Interfaces], edx |
||
715 | add ecx, sizeof.usb_device_data + 8 |
||
716 | mov edx, ecx |
||
717 | shr ecx, 2 |
||
718 | and edx, 3 |
||
719 | rep movsd |
||
720 | mov ecx, edx |
||
721 | rep movsb |
||
722 | pop edi esi |
||
723 | call usb_reinit_pipe_list |
||
724 | ; 3d. Free old memory. |
||
725 | call free |
||
726 | pop eax |
||
5177 | clevermous | 727 | ; 4. Issue control transfer GET_DESCRIPTOR(CONFIGURATION) for full descriptor. |
3520 | clevermous | 728 | movzx ecx, [eax+usb_device_data.DeviceDescrSize] |
729 | mov edx, [eax+usb_device_data.ConfigDataSize] |
||
730 | lea eax, [eax+ecx+sizeof.usb_device_data] |
||
731 | mov dword [eax], \ |
||
732 | 80h + \ ; device-to-host, standard, device-wide |
||
733 | (USB_GET_DESCRIPTOR shl 8) + \ ; request |
||
734 | (0 shl 16) + \ ; descriptor index: there is only one |
||
735 | (USB_CONFIG_DESCR shl 24) ; descriptor type |
||
736 | and dword [eax+4], 0 |
||
737 | mov word [eax+6], dx ; data length |
||
738 | stdcall usb_control_async, [pipe], eax, eax, edx, usb_set_config_callback, eax, 0 |
||
739 | .nothing: |
||
740 | ; 5. Return. |
||
741 | pop ebx ; restore used registers to be stdcall |
||
742 | ret |
||
743 | .error: |
||
744 | dbgstr 'error with USB configuration descriptor' |
||
745 | jmp .nothing |
||
746 | .nomemory: |
||
747 | dbgstr 'No memory for device data' |
||
748 | jmp .nothing |
||
749 | endp |
||
750 | |||
751 | ; This procedure is called by USB stack when GET_DESCRIPTOR(CONFIGURATION) |
||
752 | ; request initiated by usb_know_length_callback is completed, |
||
753 | ; either successfully or unsuccessfully. |
||
754 | proc usb_set_config_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
||
755 | ; Note that the prolog is the same as in usb_know_length_callback. |
||
756 | push ebx ; save used registers to be stdcall |
||
757 | ; 1. Check whether the operation was successful. |
||
758 | ; If not, say something to the debug board and stop the initialization. |
||
759 | xor ecx, ecx |
||
760 | mov ebx, [pipe] |
||
761 | cmp [status], ecx |
||
762 | jnz usb_know_length_callback.error |
||
763 | ; The full descriptor is known, dump it if specified by compile-time option. |
||
764 | if USB_DUMP_DESCRIPTORS |
||
765 | mov eax, [buffer] |
||
766 | mov ecx, [length] |
||
767 | sub ecx, 8 |
||
768 | jbe .skip_debug |
||
769 | DEBUGF 1,'K : config descriptor:' |
||
770 | @@: |
||
771 | DEBUGF 1,' %x',[eax]:2 |
||
772 | inc eax |
||
773 | dec ecx |
||
774 | jnz @b |
||
775 | DEBUGF 1,'\n' |
||
776 | .skip_debug: |
||
777 | xor ecx, ecx |
||
778 | end if |
||
779 | ; 2. Issue control transfer SET_CONFIGURATION to activate this configuration. |
||
780 | ; Usually this is the only configuration. |
||
781 | ; Use extra bytes allocated by usb_know_length_callback; |
||
782 | ; offset from device data start is stored in Interfaces. |
||
783 | mov eax, [ebx+usb_pipe.DeviceData] |
||
784 | mov edx, [buffer] |
||
785 | add eax, [eax+usb_device_data.Interfaces] |
||
786 | mov dl, [edx+usb_config_descr.bConfigurationValue] |
||
787 | mov dword [eax], USB_SET_CONFIGURATION shl 8 |
||
788 | mov dword [eax+4], ecx |
||
789 | mov byte [eax+2], dl |
||
790 | stdcall usb_control_async, [pipe], eax, ecx, ecx, usb_got_config_callback, [buffer], ecx |
||
791 | pop ebx ; restore used registers to be stdcall |
||
792 | ret |
||
793 | endp |
||
794 | |||
795 | ; This procedure is called by USB stack when SET_CONFIGURATION |
||
796 | ; request initiated by usb_set_config_callback is completed, |
||
797 | ; either successfully or unsuccessfully. |
||
798 | ; If successfully, the device is configured and ready to work, |
||
799 | ; pass the device to the corresponding driver(s). |
||
800 | proc usb_got_config_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
||
801 | locals |
||
802 | InterfacesData dd ? |
||
803 | NumInterfaces dd ? |
||
804 | driver dd ? |
||
805 | endl |
||
806 | ; 1. If there was an error, say something to the debug board and stop the |
||
807 | ; initialization. |
||
808 | cmp [status], 0 |
||
809 | jz @f |
||
810 | dbgstr 'USB error in SET_CONFIGURATION' |
||
811 | ret |
||
812 | @@: |
||
813 | push ebx edi ; save used registers to be stdcall |
||
814 | ; 2. Sanity checks: the total length must be the same as before (because we |
||
815 | ; have allocated memory assuming the old value), length of config descriptor |
||
816 | ; must be at least sizeof.usb_config_descr (we use fields from it), |
||
817 | ; there must be at least one interface. |
||
818 | mov ebx, [pipe] |
||
819 | mov ebx, [ebx+usb_pipe.DeviceData] |
||
820 | mov eax, [calldata] |
||
821 | mov edx, [ebx+usb_device_data.ConfigDataSize] |
||
822 | cmp [eax+usb_config_descr.wTotalLength], dx |
||
823 | jnz .invalid |
||
824 | cmp [eax+usb_config_descr.bLength], 9 |
||
825 | jb .invalid |
||
826 | movzx edx, [eax+usb_config_descr.bNumInterfaces] |
||
827 | test edx, edx |
||
828 | jnz @f |
||
829 | .invalid: |
||
830 | dbgstr 'error: invalid configuration descriptor' |
||
831 | jmp .nothing |
||
832 | @@: |
||
833 | ; 3. Store the number of interfaces in device data structure. |
||
3826 | clevermous | 834 | mov [ebx+usb_device_data.NumInterfaces], edx |
3520 | clevermous | 835 | ; 4. If there is only one interface (which happens quite often), |
836 | ; the memory allocated in usb_know_length_callback is sufficient. |
||
837 | ; Otherwise (which also happens quite often), reallocate device data. |
||
838 | ; 4a. Check whether there is only one interface. If so, skip this step. |
||
839 | cmp edx, 1 |
||
840 | jz .has_memory |
||
841 | ; 4b. Allocate new memory. |
||
842 | mov eax, [ebx+usb_device_data.Interfaces] |
||
843 | lea eax, [eax+edx*sizeof.usb_interface_data] |
||
844 | call malloc |
||
845 | ; 4c. If failed, say something to the debug board and |
||
846 | ; stop the initialization. |
||
847 | test eax, eax |
||
848 | jnz @f |
||
849 | dbgstr 'No memory for device data' |
||
850 | jmp .nothing |
||
851 | @@: |
||
852 | ; 4d. Copy data from old memory to new memory and switch the pointer in usb_pipe. |
||
853 | push eax |
||
854 | push esi |
||
855 | mov ebx, [pipe] |
||
856 | mov edi, eax |
||
857 | mov esi, [ebx+usb_pipe.DeviceData] |
||
858 | mov [ebx+usb_pipe.DeviceData], eax |
||
859 | mov eax, esi |
||
860 | mov ecx, [esi+usb_device_data.Interfaces] |
||
861 | shr ecx, 2 |
||
862 | rep movsd |
||
863 | pop esi |
||
864 | call usb_reinit_pipe_list |
||
865 | ; 4e. Free old memory. |
||
866 | call free |
||
867 | pop ebx |
||
868 | .has_memory: |
||
869 | ; 5. Initialize interfaces table: zero all contents. |
||
870 | mov edi, [ebx+usb_device_data.Interfaces] |
||
871 | add edi, ebx |
||
872 | mov [InterfacesData], edi |
||
3826 | clevermous | 873 | mov ecx, [ebx+usb_device_data.NumInterfaces] |
3520 | clevermous | 874 | if sizeof.usb_interface_data <> 8 |
875 | You have changed sizeof.usb_interface_data? Modify this place too. |
||
876 | end if |
||
877 | add ecx, ecx |
||
878 | xor eax, eax |
||
879 | rep stosd |
||
880 | ; No interfaces are found yet. |
||
881 | mov [NumInterfaces], eax |
||
882 | ; 6. Get the pointer to config descriptor data. |
||
883 | ; Note: if there was reallocation, [buffer] is not valid anymore, |
||
884 | ; so calculate value based on usb_device_data. |
||
885 | movzx eax, [ebx+usb_device_data.DeviceDescrSize] |
||
886 | lea eax, [eax+ebx+sizeof.usb_device_data] |
||
887 | mov [calldata], eax |
||
888 | mov ecx, [ebx+usb_device_data.ConfigDataSize] |
||
889 | ; 7. Loop over all descriptors, |
||
890 | ; scan for interface descriptors with bAlternateSetting = 0, |
||
891 | ; load the corresponding driver, call its AddDevice function. |
||
892 | .descriptor_loop: |
||
893 | ; While in loop: eax points to the current descriptor, |
||
894 | ; ecx = number of bytes left, the iteration starts only if ecx is nonzero, |
||
895 | ; edx = size of the current descriptor. |
||
896 | ; 7a. The first byte is always accessible; it contains the length of |
||
897 | ; the current descriptor. Validate that the length is at least 2 bytes, |
||
898 | ; and the entire descriptor is readable (the length is at most number of |
||
899 | ; bytes left). |
||
900 | movzx edx, [eax+usb_descr.bLength] |
||
901 | cmp edx, sizeof.usb_descr |
||
902 | jb .invalid |
||
903 | cmp ecx, edx |
||
904 | jb .invalid |
||
905 | ; 7b. Check descriptor type. Ignore all non-INTERFACE descriptor. |
||
906 | cmp byte [eax+usb_descr.bDescriptorType], USB_INTERFACE_DESCR |
||
907 | jz .interface |
||
908 | .next_descriptor: |
||
909 | ; 7c. Advance pointer, decrease length left, if there is still something left, |
||
910 | ; continue the loop. |
||
911 | add eax, edx |
||
912 | sub ecx, edx |
||
913 | jnz .descriptor_loop |
||
914 | .done: |
||
915 | .nothing: |
||
916 | pop edi ebx ; restore used registers to be stdcall |
||
917 | ret |
||
918 | .interface: |
||
919 | ; 7d. Validate the descriptor length. |
||
920 | cmp edx, sizeof.usb_interface_descr |
||
921 | jb .next_descriptor |
||
922 | ; 7e. If bAlternateSetting is nonzero, this descriptor actually describes |
||
923 | ; another mode of already known interface and belongs to the already loaded |
||
924 | ; driver; amuse the user and continue to 7c. |
||
925 | cmp byte [eax+usb_interface_descr.bAlternateSetting], 0 |
||
926 | jz @f |
||
927 | DEBUGF 1,'K : note: alternate setting with %x/%x/%x\n',\ |
||
928 | [eax+usb_interface_descr.bInterfaceClass]:2,\ |
||
929 | [eax+usb_interface_descr.bInterfaceSubClass]:2,\ |
||
930 | [eax+usb_interface_descr.bInterfaceProtocol]:2 |
||
931 | jmp .next_descriptor |
||
932 | @@: |
||
933 | ; 7f. Check that the new interface does not overflow allocated table. |
||
934 | mov edx, [NumInterfaces] |
||
3826 | clevermous | 935 | inc edx |
936 | cmp edx, [ebx+usb_device_data.NumInterfaces] |
||
3520 | clevermous | 937 | ja .invalid |
938 | ; 7g. We have found a new interface. Advance bookkeeping vars. |
||
939 | mov [NumInterfaces], edx |
||
940 | add [InterfacesData], sizeof.usb_interface_data |
||
941 | ; 7h. Save length left and pointer to the current interface descriptor. |
||
942 | push ecx eax |
||
943 | ; Amuse the user if she is watching the debug board. |
||
944 | DEBUGF 1,'K : USB interface class/subclass/protocol = %x/%x/%x\n',\ |
||
945 | [eax+usb_interface_descr.bInterfaceClass]:2,\ |
||
946 | [eax+usb_interface_descr.bInterfaceSubClass]:2,\ |
||
947 | [eax+usb_interface_descr.bInterfaceProtocol]:2 |
||
948 | ; 7i. Select the correct driver based on interface class. |
||
949 | ; For hubs, go to 7j. Otherwise, go to 7k. |
||
950 | ; Note: this should be rewritten as table-based lookup when more drivers will |
||
951 | ; be available. |
||
952 | cmp byte [eax+usb_interface_descr.bInterfaceClass], 9 |
||
953 | jz .found_hub |
||
954 | mov edx, usb_hid_name |
||
955 | cmp byte [eax+usb_interface_descr.bInterfaceClass], 3 |
||
956 | jz .load_driver |
||
957 | mov edx, usb_print_name |
||
958 | cmp byte [eax+usb_interface_descr.bInterfaceClass], 7 |
||
959 | jz .load_driver |
||
960 | mov edx, usb_stor_name |
||
961 | cmp byte [eax+usb_interface_descr.bInterfaceClass], 8 |
||
962 | jz .load_driver |
||
963 | mov edx, usb_other_name |
||
964 | jmp .load_driver |
||
965 | .found_hub: |
||
966 | ; 7j. Hubs are a part of USB stack, thus, integrated into the kernel. |
||
967 | ; Use the pointer to hub callbacks and go to 7m. |
||
968 | mov eax, usb_hub_pseudosrv - USBSRV.usb_func |
||
969 | jmp .driver_loaded |
||
970 | .load_driver: |
||
971 | ; 7k. Load the corresponding driver. |
||
972 | push ebx esi edi |
||
973 | stdcall get_service, edx |
||
974 | pop edi esi ebx |
||
975 | ; 7l. If failed, say something to the debug board and go to 7p. |
||
976 | test eax, eax |
||
977 | jnz .driver_loaded |
||
978 | dbgstr 'failed to load class driver' |
||
979 | jmp .next_descriptor2 |
||
980 | .driver_loaded: |
||
981 | ; 7m. Call AddDevice function of the driver. |
||
982 | ; Note that top of stack contains a pointer to the current interface, |
||
983 | ; saved by step 7h. |
||
984 | mov [driver], eax |
||
985 | mov eax, [eax+USBSRV.usb_func] |
||
986 | pop edx |
||
987 | push edx |
||
988 | ; Note: usb_hub_init assumes that edx points to usb_interface_descr, |
||
989 | ; ecx = length rest; if you change the code, modify usb_hub_init also. |
||
990 | stdcall [eax+USBFUNC.add_device], [pipe], [calldata], edx |
||
991 | ; 7n. If failed, say something to the debug board and go to 7p. |
||
992 | test eax, eax |
||
993 | jnz .store_data |
||
994 | dbgstr 'USB device initialization failed' |
||
995 | jmp .next_descriptor2 |
||
996 | .store_data: |
||
997 | ; 7o. Store the returned value and the driver handle to InterfacesData. |
||
998 | ; Note that step 7g has already advanced InterfacesData. |
||
999 | mov edx, [InterfacesData] |
||
1000 | mov [edx+usb_interface_data.DriverData-sizeof.usb_interface_data], eax |
||
1001 | mov eax, [driver] |
||
1002 | mov [edx+usb_interface_data.DriverFunc-sizeof.usb_interface_data], eax |
||
1003 | .next_descriptor2: |
||
1004 | ; 7p. Restore registers saved in step 7h, get the descriptor length and |
||
1005 | ; continue to 7c. |
||
1006 | pop eax ecx |
||
1007 | movzx edx, byte [eax+usb_descr.bLength] |
||
1008 | jmp .next_descriptor |
||
1009 | endp |
||
1010 | |||
1011 | ; Driver names, see step 7i of usb_got_config_callback. |
||
1012 | iglobal |
||
1013 | usb_hid_name db 'usbhid',0 |
||
1014 | usb_stor_name db 'usbstor',0 |
||
1015 | usb_print_name db 'usbprint',0 |
||
1016 | usb_other_name db 'usbother',0 |
||
1017 | endg>=>=>=>=> |