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