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