Rev 4592 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4592 | Rev 5051 | ||
---|---|---|---|
1 | ; standard driver stuff |
1 | ; standard driver stuff; version of driver model = 5 |
2 | format MS COFF |
2 | format PE DLL native 0.05 |
- | 3 | entry START |
|
3 | 4 | ||
4 | DEBUG = 1 |
5 | DEBUG = 1 |
5 | 6 | ||
6 | ; this is for DEBUGF macro from 'fdo.inc' |
7 | ; this is for DEBUGF macro from 'fdo.inc' |
7 | __DEBUG__ = 1 |
8 | __DEBUG__ = 1 |
8 | __DEBUG_LEVEL__ = 1 |
9 | __DEBUG_LEVEL__ = 1 |
9 | - | ||
10 | include '../proc32.inc' |
- | |
11 | include '../imports.inc' |
- | |
12 | include '../fdo.inc' |
10 | |
13 | include '../../struct.inc' |
- | |
14 | - | ||
15 | public START |
- | |
16 | public version |
11 | include '../../struct.inc' |
17 | 12 | ||
18 | ; Compile-time settings. |
13 | ; Compile-time settings. |
19 | ; If set, the code will dump all descriptors as they are read to the debug board. |
14 | ; If set, the code will dump all descriptors as they are read to the debug board. |
20 | USB_DUMP_DESCRIPTORS = 1 |
15 | USB_DUMP_DESCRIPTORS = 1 |
21 | ; If set, the code will dump any unclaimed input to the debug board. |
16 | ; If set, the code will dump any unclaimed input to the debug board. |
22 | HID_DUMP_UNCLAIMED = 1 |
17 | HID_DUMP_UNCLAIMED = 1 |
23 | 18 | ||
24 | ; USB constants |
19 | ; USB constants |
25 | DEVICE_DESCR_TYPE = 1 |
20 | DEVICE_DESCR_TYPE = 1 |
26 | CONFIG_DESCR_TYPE = 2 |
21 | CONFIG_DESCR_TYPE = 2 |
27 | STRING_DESCR_TYPE = 3 |
22 | STRING_DESCR_TYPE = 3 |
28 | INTERFACE_DESCR_TYPE = 4 |
23 | INTERFACE_DESCR_TYPE = 4 |
29 | ENDPOINT_DESCR_TYPE = 5 |
24 | ENDPOINT_DESCR_TYPE = 5 |
30 | DEVICE_QUALIFIER_DESCR_TYPE = 6 |
25 | DEVICE_QUALIFIER_DESCR_TYPE = 6 |
31 | 26 | ||
32 | CONTROL_PIPE = 0 |
27 | CONTROL_PIPE = 0 |
33 | ISOCHRONOUS_PIPE = 1 |
28 | ISOCHRONOUS_PIPE = 1 |
34 | BULK_PIPE = 2 |
29 | BULK_PIPE = 2 |
35 | INTERRUPT_PIPE = 3 |
30 | INTERRUPT_PIPE = 3 |
36 | 31 | ||
37 | ; USB HID constants |
32 | ; USB HID constants |
38 | HID_DESCR_TYPE = 21h |
33 | HID_DESCR_TYPE = 21h |
39 | REPORT_DESCR_TYPE = 22h |
34 | REPORT_DESCR_TYPE = 22h |
40 | PHYSICAL_DESCR_TYPE = 23h |
35 | PHYSICAL_DESCR_TYPE = 23h |
41 | 36 | ||
42 | ; USB structures |
37 | ; USB structures |
43 | struct config_descr |
38 | struct config_descr |
44 | bLength db ? |
39 | bLength db ? |
45 | bDescriptorType db ? |
40 | bDescriptorType db ? |
46 | wTotalLength dw ? |
41 | wTotalLength dw ? |
47 | bNumInterfaces db ? |
42 | bNumInterfaces db ? |
48 | bConfigurationValue db ? |
43 | bConfigurationValue db ? |
49 | iConfiguration db ? |
44 | iConfiguration db ? |
50 | bmAttributes db ? |
45 | bmAttributes db ? |
51 | bMaxPower db ? |
46 | bMaxPower db ? |
52 | ends |
47 | ends |
53 | 48 | ||
54 | struct interface_descr |
49 | struct interface_descr |
55 | bLength db ? |
50 | bLength db ? |
56 | bDescriptorType db ? |
51 | bDescriptorType db ? |
57 | bInterfaceNumber db ? |
52 | bInterfaceNumber db ? |
58 | bAlternateSetting db ? |
53 | bAlternateSetting db ? |
59 | bNumEndpoints db ? |
54 | bNumEndpoints db ? |
60 | bInterfaceClass db ? |
55 | bInterfaceClass db ? |
61 | bInterfaceSubClass db ? |
56 | bInterfaceSubClass db ? |
62 | bInterfaceProtocol db ? |
57 | bInterfaceProtocol db ? |
63 | iInterface db ? |
58 | iInterface db ? |
64 | ends |
59 | ends |
65 | 60 | ||
66 | struct endpoint_descr |
61 | struct endpoint_descr |
67 | bLength db ? |
62 | bLength db ? |
68 | bDescriptorType db ? |
63 | bDescriptorType db ? |
69 | bEndpointAddress db ? |
64 | bEndpointAddress db ? |
70 | bmAttributes db ? |
65 | bmAttributes db ? |
71 | wMaxPacketSize dw ? |
66 | wMaxPacketSize dw ? |
72 | bInterval db ? |
67 | bInterval db ? |
73 | ends |
68 | ends |
74 | 69 | ||
75 | ; USB HID structures |
70 | ; USB HID structures |
76 | struct hid_descr |
71 | struct hid_descr |
77 | bLength db ? |
72 | bLength db ? |
78 | bDescriptorType db ? |
73 | bDescriptorType db ? |
79 | bcdHID dw ? |
74 | bcdHID dw ? |
80 | bCountryCode db ? |
75 | bCountryCode db ? |
81 | bNumDescriptors db ? |
76 | bNumDescriptors db ? |
82 | base_sizeof rb 0 |
77 | base_sizeof rb 0 |
83 | ; now two fields are repeated .bNumDescriptors times: |
78 | ; now two fields are repeated .bNumDescriptors times: |
84 | subDescriptorType db ? |
79 | subDescriptorType db ? |
85 | subDescriptorLength dw ? |
80 | subDescriptorLength dw ? |
86 | ends |
81 | ends |
87 | 82 | ||
88 | ; Include macro for parsing report descriptors/data. |
83 | ; Include macro for parsing report descriptors/data. |
89 | macro workers_globals |
84 | macro workers_globals |
90 | {} |
85 | {} |
91 | include 'report.inc' |
86 | include 'report.inc' |
92 | 87 | ||
93 | ; Driver data for all devices |
88 | ; Driver data for all devices |
94 | struct usb_device_data |
89 | struct usb_device_data |
95 | hid hid_data ; data of HID layer |
90 | hid hid_data ; data of HID layer |
96 | epdescr dd ? ; endpoint descriptor |
91 | epdescr dd ? ; endpoint descriptor |
97 | hiddescr dd ? ; HID descriptor |
92 | hiddescr dd ? ; HID descriptor |
98 | interface_number dd ? ; copy of interface_descr.bInterfaceNumber |
93 | interface_number dd ? ; copy of interface_descr.bInterfaceNumber |
99 | configpipe dd ? ; config pipe handle |
94 | configpipe dd ? ; config pipe handle |
100 | intpipe dd ? ; interrupt pipe handle |
95 | intpipe dd ? ; interrupt pipe handle |
101 | input_transfer_size dd ? ; input transfer size |
96 | input_transfer_size dd ? ; input transfer size |
102 | input_buffer dd ? ; buffer for input transfers |
97 | input_buffer dd ? ; buffer for input transfers |
103 | control rb 8 ; control packet to device |
98 | control rb 8 ; control packet to device |
104 | ends |
99 | ends |
105 | 100 | ||
- | 101 | section '.flat' code readable writable executable |
|
- | 102 | include '../../macros.inc' |
|
- | 103 | include '../../proc32.inc' |
|
- | 104 | include '../../peimport.inc' |
|
106 | section '.flat' code readable align 16 |
105 | include '../../fdo.inc' |
107 | ; The start procedure. |
106 | ; The start procedure. |
108 | proc START |
107 | proc START |
109 | virtual at esp |
108 | virtual at esp |
110 | dd ? ; return address |
109 | dd ? ; return address |
111 | .reason dd ? |
110 | .reason dd ? |
- | 111 | .cmdline dd ? |
|
112 | end virtual |
112 | end virtual |
113 | ; 1. Test whether the procedure is called with the argument DRV_ENTRY. |
113 | ; 1. Test whether the procedure is called with the argument DRV_ENTRY. |
114 | ; If not, return 0. |
114 | ; If not, return 0. |
115 | xor eax, eax ; initialize return value |
115 | xor eax, eax ; initialize return value |
116 | cmp [.reason], 1 ; compare the argument |
116 | cmp [.reason], 1 ; compare the argument |
117 | jnz .nothing |
117 | jnz .nothing |
118 | ; 2. Register self as a USB driver. |
118 | ; 2. Register self as a USB driver. |
119 | ; The name is my_driver = 'usbhid'; IOCTL interface is not supported; |
119 | ; The name is my_driver = 'usbhid'; IOCTL interface is not supported; |
120 | ; usb_functions is an offset of a structure with callback functions. |
120 | ; usb_functions is an offset of a structure with callback functions. |
121 | stdcall RegUSBDriver, my_driver, eax, usb_functions |
121 | invoke RegUSBDriver, my_driver, eax, usb_functions |
122 | ; 3. Return the returned value of RegUSBDriver. |
122 | ; 3. Return the returned value of RegUSBDriver. |
123 | .nothing: |
123 | .nothing: |
124 | ret 4 |
124 | ret |
125 | endp |
125 | endp |
126 | 126 | ||
127 | ; This procedure is called when new HID device is detected. |
127 | ; This procedure is called when new HID device is detected. |
128 | ; It initializes the device. |
128 | ; It initializes the device. |
129 | proc AddDevice |
129 | proc AddDevice |
130 | push ebx esi edi ; save used registers to be stdcall |
130 | push ebx esi edi ; save used registers to be stdcall |
131 | virtual at esp |
131 | virtual at esp |
132 | rd 3 ; saved registers |
132 | rd 3 ; saved registers |
133 | dd ? ; return address |
133 | dd ? ; return address |
134 | .config_pipe dd ? |
134 | .config_pipe dd ? |
135 | .config_descr dd ? |
135 | .config_descr dd ? |
136 | .interface dd ? |
136 | .interface dd ? |
137 | end virtual |
137 | end virtual |
138 | DEBUGF 1,'K : USB HID device detected\n' |
138 | DEBUGF 1,'K : USB HID device detected\n' |
139 | ; 1. Allocate memory for device data. |
139 | ; 1. Allocate memory for device data. |
140 | movi eax, sizeof.usb_device_data |
140 | movi eax, sizeof.usb_device_data |
141 | call Kmalloc |
141 | invoke Kmalloc |
142 | test eax, eax |
142 | test eax, eax |
143 | jnz @f |
143 | jnz @f |
144 | mov esi, nomemory_msg |
144 | mov esi, nomemory_msg |
145 | call SysMsgBoardStr |
145 | invoke SysMsgBoardStr |
146 | jmp .return0 |
146 | jmp .return0 |
147 | @@: |
147 | @@: |
148 | ; zero-initialize it |
148 | ; zero-initialize it |
149 | mov edi, eax |
149 | mov edi, eax |
150 | xchg eax, ebx |
150 | xchg eax, ebx |
151 | xor eax, eax |
151 | xor eax, eax |
152 | movi ecx, sizeof.usb_device_data / 4 |
152 | movi ecx, sizeof.usb_device_data / 4 |
153 | rep stosd |
153 | rep stosd |
154 | mov edx, [.interface] |
154 | mov edx, [.interface] |
155 | ; HID devices use one IN interrupt endpoint for polling the device |
155 | ; HID devices use one IN interrupt endpoint for polling the device |
156 | ; and an optional OUT interrupt endpoint. We do not use the later, |
156 | ; and an optional OUT interrupt endpoint. We do not use the later, |
157 | ; but must locate the first. Look for the IN interrupt endpoint. |
157 | ; but must locate the first. Look for the IN interrupt endpoint. |
158 | ; Also, look for the HID descriptor; according to HID spec, it must be |
158 | ; Also, look for the HID descriptor; according to HID spec, it must be |
159 | ; located before endpoint descriptors. |
159 | ; located before endpoint descriptors. |
160 | ; 2. Get the upper bound of all descriptors' data. |
160 | ; 2. Get the upper bound of all descriptors' data. |
161 | mov eax, [.config_descr] |
161 | mov eax, [.config_descr] |
162 | movzx ecx, [eax+config_descr.wTotalLength] |
162 | movzx ecx, [eax+config_descr.wTotalLength] |
163 | add eax, ecx |
163 | add eax, ecx |
164 | ; 3. Loop over all descriptors until |
164 | ; 3. Loop over all descriptors until |
165 | ; either end-of-data reached - this is fail |
165 | ; either end-of-data reached - this is fail |
166 | ; or interface descriptor found - this is fail, all further data |
166 | ; or interface descriptor found - this is fail, all further data |
167 | ; correspond to that interface |
167 | ; correspond to that interface |
168 | ; or endpoint descriptor for IN endpoint is found |
168 | ; or endpoint descriptor for IN endpoint is found |
169 | ; (HID descriptor must be located before the endpoint descriptor). |
169 | ; (HID descriptor must be located before the endpoint descriptor). |
170 | ; 3a. Loop start: edx points to the interface descriptor. |
170 | ; 3a. Loop start: edx points to the interface descriptor. |
171 | .lookep: |
171 | .lookep: |
172 | ; 3b. Get next descriptor. |
172 | ; 3b. Get next descriptor. |
173 | movzx ecx, byte [edx] ; the first byte of all descriptors is length |
173 | movzx ecx, byte [edx] ; the first byte of all descriptors is length |
174 | test ecx, ecx |
174 | test ecx, ecx |
175 | jz .cfgerror |
175 | jz .cfgerror |
176 | add edx, ecx |
176 | add edx, ecx |
177 | ; 3c. Check that at least two bytes are readable. The opposite is an error. |
177 | ; 3c. Check that at least two bytes are readable. The opposite is an error. |
178 | inc edx |
178 | inc edx |
179 | cmp edx, eax |
179 | cmp edx, eax |
180 | jae .cfgerror |
180 | jae .cfgerror |
181 | dec edx |
181 | dec edx |
182 | ; 3d. Check that this descriptor is not interface descriptor. The opposite is |
182 | ; 3d. Check that this descriptor is not interface descriptor. The opposite is |
183 | ; an error. |
183 | ; an error. |
184 | cmp [edx+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE |
184 | cmp [edx+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE |
185 | jz .cfgerror |
185 | jz .cfgerror |
186 | ; 3e. For HID descriptor, proceed to 4. |
186 | ; 3e. For HID descriptor, proceed to 4. |
187 | ; For endpoint descriptor, go to 5. |
187 | ; For endpoint descriptor, go to 5. |
188 | ; For other descriptors, continue the loop. |
188 | ; For other descriptors, continue the loop. |
189 | ; Note: bDescriptorType is in the same place in all descriptors. |
189 | ; Note: bDescriptorType is in the same place in all descriptors. |
190 | cmp [edx+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE |
190 | cmp [edx+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE |
191 | jz .foundep |
191 | jz .foundep |
192 | cmp [edx+endpoint_descr.bDescriptorType], HID_DESCR_TYPE |
192 | cmp [edx+endpoint_descr.bDescriptorType], HID_DESCR_TYPE |
193 | jnz .lookep |
193 | jnz .lookep |
194 | ; 4a. Check that the descriptor contains all required data and all data are |
194 | ; 4a. Check that the descriptor contains all required data and all data are |
195 | ; readable. The opposite is an error. |
195 | ; readable. The opposite is an error. |
196 | movzx ecx, [edx+hid_descr.bLength] |
196 | movzx ecx, [edx+hid_descr.bLength] |
197 | cmp ecx, hid_descr.base_sizeof + 3 |
197 | cmp ecx, hid_descr.base_sizeof + 3 |
198 | jb .cfgerror |
198 | jb .cfgerror |
199 | add ecx, edx |
199 | add ecx, edx |
200 | cmp ecx, eax |
200 | cmp ecx, eax |
201 | ja .cfgerror |
201 | ja .cfgerror |
202 | ; 4b. Store the pointer in usb_device_data structure for further references. |
202 | ; 4b. Store the pointer in usb_device_data structure for further references. |
203 | mov [ebx+usb_device_data.hiddescr], edx |
203 | mov [ebx+usb_device_data.hiddescr], edx |
204 | ; 4c. Continue the loop. |
204 | ; 4c. Continue the loop. |
205 | jmp .lookep |
205 | jmp .lookep |
206 | .foundep: |
206 | .foundep: |
207 | ; 5a. Check that the descriptor contains all required data and all data are |
207 | ; 5a. Check that the descriptor contains all required data and all data are |
208 | ; readable. The opposite is an error. |
208 | ; readable. The opposite is an error. |
209 | cmp byte [edx+endpoint_descr.bLength], sizeof.endpoint_descr |
209 | cmp byte [edx+endpoint_descr.bLength], sizeof.endpoint_descr |
210 | jb .cfgerror |
210 | jb .cfgerror |
211 | lea ecx, [edx+sizeof.endpoint_descr] |
211 | lea ecx, [edx+sizeof.endpoint_descr] |
212 | cmp ecx, eax |
212 | cmp ecx, eax |
213 | jbe @f |
213 | jbe @f |
214 | ; 6. An error occured during processing endpoint descriptor. |
214 | ; 6. An error occured during processing endpoint descriptor. |
215 | .cfgerror: |
215 | .cfgerror: |
216 | ; 6a. Print a message. |
216 | ; 6a. Print a message. |
217 | mov esi, invalid_config_descr_msg |
217 | mov esi, invalid_config_descr_msg |
218 | call SysMsgBoardStr |
218 | invoke SysMsgBoardStr |
219 | ; 6b. Free memory allocated for device data. |
219 | ; 6b. Free memory allocated for device data. |
220 | .free: |
220 | .free: |
221 | xchg eax, ebx |
221 | xchg eax, ebx |
222 | call Kfree |
222 | invoke Kfree |
223 | .return0: |
223 | .return0: |
224 | ; 6c. Return an error. |
224 | ; 6c. Return an error. |
225 | xor eax, eax |
225 | xor eax, eax |
226 | .nothing: |
226 | .nothing: |
227 | pop edi esi ebx ; restore used registers to be stdcall |
227 | pop edi esi ebx ; restore used registers to be stdcall |
228 | ret 12 |
228 | ret 12 |
229 | @@: |
229 | @@: |
230 | ; 5b. If this is not IN interrupt endpoint, ignore it and continue the loop. |
230 | ; 5b. If this is not IN interrupt endpoint, ignore it and continue the loop. |
231 | test [edx+endpoint_descr.bEndpointAddress], 80h |
231 | test [edx+endpoint_descr.bEndpointAddress], 80h |
232 | jz .lookep |
232 | jz .lookep |
233 | mov cl, [edx+endpoint_descr.bmAttributes] |
233 | mov cl, [edx+endpoint_descr.bmAttributes] |
234 | and cl, 3 |
234 | and cl, 3 |
235 | cmp cl, INTERRUPT_PIPE |
235 | cmp cl, INTERRUPT_PIPE |
236 | jnz .lookep |
236 | jnz .lookep |
237 | ; 5c. Store the pointer in usb_device_data structure for futher references. |
237 | ; 5c. Store the pointer in usb_device_data structure for futher references. |
238 | mov [ebx+usb_device_data.epdescr], edx |
238 | mov [ebx+usb_device_data.epdescr], edx |
239 | ; 5d. Check that HID descriptor was found. If not, go to 6. |
239 | ; 5d. Check that HID descriptor was found. If not, go to 6. |
240 | cmp [ebx+usb_device_data.hiddescr], 0 |
240 | cmp [ebx+usb_device_data.hiddescr], 0 |
241 | jz .cfgerror |
241 | jz .cfgerror |
242 | .descriptors_found: |
242 | .descriptors_found: |
243 | ; 6. Configuration descriptor seems to be ok. |
243 | ; 6. Configuration descriptor seems to be ok. |
244 | ; Send SET_IDLE command disabling auto-repeat feature (it is quite useless) |
244 | ; Send SET_IDLE command disabling auto-repeat feature (it is quite useless) |
245 | ; and continue configuring in SET_IDLE callback. |
245 | ; and continue configuring in SET_IDLE callback. |
246 | lea edx, [ebx+usb_device_data.control] |
246 | lea edx, [ebx+usb_device_data.control] |
247 | mov eax, [.interface] |
247 | mov eax, [.interface] |
248 | mov dword [edx], 21h + \ ; Class-specific request to Interface |
248 | mov dword [edx], 21h + \ ; Class-specific request to Interface |
249 | (0Ah shl 8) + \ ; SET_IDLE |
249 | (0Ah shl 8) + \ ; SET_IDLE |
250 | (0 shl 16) + \ ; apply to all input reports |
250 | (0 shl 16) + \ ; apply to all input reports |
251 | (0 shl 24) ; disable auto-repeat |
251 | (0 shl 24) ; disable auto-repeat |
252 | movzx eax, [eax+interface_descr.bInterfaceNumber] |
252 | movzx eax, [eax+interface_descr.bInterfaceNumber] |
253 | mov [ebx+usb_device_data.interface_number], eax |
253 | mov [ebx+usb_device_data.interface_number], eax |
254 | mov [edx+4], eax ; set interface number, zero length |
254 | mov [edx+4], eax ; set interface number, zero length |
255 | mov eax, [.config_pipe] |
255 | mov eax, [.config_pipe] |
256 | mov [ebx+usb_device_data.configpipe], eax |
256 | mov [ebx+usb_device_data.configpipe], eax |
257 | xor ecx, ecx |
257 | xor ecx, ecx |
258 | stdcall USBControlTransferAsync, eax, edx, ecx, ecx, idle_set, ebx, ecx |
258 | invoke USBControlTransferAsync, eax, edx, ecx, ecx, idle_set, ebx, ecx |
259 | ; 7. Return pointer to usb_device_data. |
259 | ; 7. Return pointer to usb_device_data. |
260 | xchg eax, ebx |
260 | xchg eax, ebx |
261 | jmp .nothing |
261 | jmp .nothing |
262 | endp |
262 | endp |
263 | 263 | ||
264 | ; This procedure is called by USB stack when SET_IDLE request initiated by |
264 | ; This procedure is called by USB stack when SET_IDLE request initiated by |
265 | ; AddDevice is completed, either successfully or unsuccessfully. |
265 | ; AddDevice is completed, either successfully or unsuccessfully. |
266 | proc idle_set |
266 | proc idle_set |
267 | push ebx esi ; save used registers to be stdcall |
267 | push ebx esi ; save used registers to be stdcall |
268 | virtual at esp |
268 | virtual at esp |
269 | rd 2 ; saved registers |
269 | rd 2 ; saved registers |
270 | dd ? ; return address |
270 | dd ? ; return address |
271 | .pipe dd ? |
271 | .pipe dd ? |
272 | .status dd ? |
272 | .status dd ? |
273 | .buffer dd ? |
273 | .buffer dd ? |
274 | .length dd ? |
274 | .length dd ? |
275 | .calldata dd ? |
275 | .calldata dd ? |
276 | end virtual |
276 | end virtual |
277 | ; Ignore status. Support for SET_IDLE is optional, so the device is free to |
277 | ; Ignore status. Support for SET_IDLE is optional, so the device is free to |
278 | ; STALL the request; config pipe should remain functional without explicit cleanup. |
278 | ; STALL the request; config pipe should remain functional without explicit cleanup. |
279 | mov ebx, [.calldata] |
279 | mov ebx, [.calldata] |
280 | ; 1. HID descriptor contains length of Report descriptor. Parse it. |
280 | ; 1. HID descriptor contains length of Report descriptor. Parse it. |
281 | mov esi, [ebx+usb_device_data.hiddescr] |
281 | mov esi, [ebx+usb_device_data.hiddescr] |
282 | movzx ecx, [esi+hid_descr.bNumDescriptors] |
282 | movzx ecx, [esi+hid_descr.bNumDescriptors] |
283 | lea eax, [hid_descr.base_sizeof+ecx*3] |
283 | lea eax, [hid_descr.base_sizeof+ecx*3] |
284 | cmp eax, 100h |
284 | cmp eax, 100h |
285 | jae .cfgerror |
285 | jae .cfgerror |
286 | cmp al, [esi+hid_descr.bLength] |
286 | cmp al, [esi+hid_descr.bLength] |
287 | jb .cfgerror |
287 | jb .cfgerror |
288 | .look_report: |
288 | .look_report: |
289 | dec ecx |
289 | dec ecx |
290 | js .cfgerror |
290 | js .cfgerror |
291 | cmp [esi+hid_descr.subDescriptorType], REPORT_DESCR_TYPE |
291 | cmp [esi+hid_descr.subDescriptorType], REPORT_DESCR_TYPE |
292 | jz .found_report |
292 | jz .found_report |
293 | add esi, 3 |
293 | add esi, 3 |
294 | jmp .look_report |
294 | jmp .look_report |
295 | .cfgerror: |
295 | .cfgerror: |
296 | mov esi, invalid_config_descr_msg |
296 | mov esi, invalid_config_descr_msg |
297 | .abort_with_msg: |
297 | .abort_with_msg: |
298 | call SysMsgBoardStr |
298 | invoke SysMsgBoardStr |
299 | jmp .nothing |
299 | jmp .nothing |
300 | .found_report: |
300 | .found_report: |
301 | ; 2. Send request for the Report descriptor. |
301 | ; 2. Send request for the Report descriptor. |
302 | ; 2a. Allocate memory. |
302 | ; 2a. Allocate memory. |
303 | movzx eax, [esi+hid_descr.subDescriptorLength] |
303 | movzx eax, [esi+hid_descr.subDescriptorLength] |
304 | test eax, eax |
304 | test eax, eax |
305 | jz .cfgerror |
305 | jz .cfgerror |
306 | push eax |
306 | push eax |
307 | call Kmalloc |
307 | invoke Kmalloc |
308 | pop ecx |
308 | pop ecx |
309 | ; If failed, say a message and stop initialization. |
309 | ; If failed, say a message and stop initialization. |
310 | mov esi, nomemory_msg |
310 | mov esi, nomemory_msg |
311 | test eax, eax |
311 | test eax, eax |
312 | jz .abort_with_msg |
312 | jz .abort_with_msg |
313 | ; 2b. Submit the request. |
313 | ; 2b. Submit the request. |
314 | xchg eax, esi |
314 | xchg eax, esi |
315 | lea edx, [ebx+usb_device_data.control] |
315 | lea edx, [ebx+usb_device_data.control] |
316 | mov eax, [ebx+usb_device_data.interface_number] |
316 | mov eax, [ebx+usb_device_data.interface_number] |
317 | mov dword [edx], 81h + \ ; Standard request to Interface |
317 | mov dword [edx], 81h + \ ; Standard request to Interface |
318 | (6 shl 8) + \ ; GET_DESCRIPTOR |
318 | (6 shl 8) + \ ; GET_DESCRIPTOR |
319 | (0 shl 16) + \ ; descriptor index: there is only one report descriptor |
319 | (0 shl 16) + \ ; descriptor index: there is only one report descriptor |
320 | (REPORT_DESCR_TYPE shl 24); descriptor type |
320 | (REPORT_DESCR_TYPE shl 24); descriptor type |
321 | mov [edx+4], ax ; Interface number |
321 | mov [edx+4], ax ; Interface number |
322 | mov [edx+6], cx ; descriptor length |
322 | mov [edx+6], cx ; descriptor length |
323 | stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \ |
323 | invoke USBControlTransferAsync, [ebx+usb_device_data.configpipe], \ |
324 | edx, esi, ecx, got_report, ebx, 0 |
324 | edx, esi, ecx, got_report, ebx, 0 |
325 | ; 2c. If failed, free the buffer and stop initialization. |
325 | ; 2c. If failed, free the buffer and stop initialization. |
326 | test eax, eax |
326 | test eax, eax |
327 | jnz .nothing |
327 | jnz .nothing |
328 | xchg eax, esi |
328 | xchg eax, esi |
329 | call Kfree |
329 | invoke Kfree |
330 | .nothing: |
330 | .nothing: |
331 | pop esi ebx ; restore used registers to be stdcall |
331 | pop esi ebx ; restore used registers to be stdcall |
332 | ret 20 |
332 | ret 20 |
333 | endp |
333 | endp |
334 | 334 | ||
335 | ; This procedure is called by USB stack when the report descriptor queried |
335 | ; This procedure is called by USB stack when the report descriptor queried |
336 | ; by idle_set is completed, either successfully or unsuccessfully. |
336 | ; by idle_set is completed, either successfully or unsuccessfully. |
337 | proc got_report stdcall uses ebx esi edi, pipe, status, buffer, length, calldata |
337 | proc got_report stdcall uses ebx esi edi, pipe, status, buffer, length, calldata |
338 | locals |
338 | locals |
339 | parse_descr_locals |
339 | parse_descr_locals |
340 | if ~HID_DUMP_UNCLAIMED |
340 | if ~HID_DUMP_UNCLAIMED |
341 | has_driver db ? |
341 | has_driver db ? |
342 | rb 3 |
342 | rb 3 |
343 | end if |
343 | end if |
344 | endl |
344 | endl |
345 | ; 1. Check the status; if the request has failed, say something to the debug board |
345 | ; 1. Check the status; if the request has failed, say something to the debug board |
346 | ; and stop initialization. |
346 | ; and stop initialization. |
347 | cmp [status], 0 |
347 | cmp [status], 0 |
348 | jnz .generic_fail |
348 | jnz .generic_fail |
349 | ; 2. Subtract size of setup packet from the total length; |
349 | ; 2. Subtract size of setup packet from the total length; |
350 | ; the rest is length of the descriptor, and it must be nonzero. |
350 | ; the rest is length of the descriptor, and it must be nonzero. |
351 | sub [length], 8 |
351 | sub [length], 8 |
352 | ja .has_something |
352 | ja .has_something |
353 | .generic_fail: |
353 | .generic_fail: |
354 | push esi |
354 | push esi |
355 | mov esi, reportfail |
355 | mov esi, reportfail |
356 | call SysMsgBoardStr |
356 | invoke SysMsgBoardStr |
357 | pop esi |
357 | pop esi |
358 | jmp .exit |
358 | jmp .exit |
359 | .has_something: |
359 | .has_something: |
360 | ; 3. Process descriptor. |
360 | ; 3. Process descriptor. |
361 | ; 3a. Dump it to the debug board, if enabled in compile-time setting. |
361 | ; 3a. Dump it to the debug board, if enabled in compile-time setting. |
362 | if USB_DUMP_DESCRIPTORS |
362 | if USB_DUMP_DESCRIPTORS |
363 | mov eax, [buffer] |
363 | mov eax, [buffer] |
364 | mov ecx, [length] |
364 | mov ecx, [length] |
365 | DEBUGF 1,'K : report descriptor:' |
365 | DEBUGF 1,'K : report descriptor:' |
366 | @@: |
366 | @@: |
367 | DEBUGF 1,' %x',[eax]:2 |
367 | DEBUGF 1,' %x',[eax]:2 |
368 | inc eax |
368 | inc eax |
369 | dec ecx |
369 | dec ecx |
370 | jnz @b |
370 | jnz @b |
371 | DEBUGF 1,'\n' |
371 | DEBUGF 1,'\n' |
372 | end if |
372 | end if |
373 | ; 3b. Call the HID layer. |
373 | ; 3b. Call the HID layer. |
374 | parse_descr |
374 | parse_descr |
375 | cmp [report_ok], 0 |
375 | cmp [report_ok], 0 |
376 | jz got_report.exit |
376 | jz got_report.exit |
377 | mov ebx, [calldata] |
377 | mov ebx, [calldata] |
378 | postprocess_descr |
378 | postprocess_descr |
379 | ; 4. Stop initialization if no driver is assigned. |
379 | ; 4. Stop initialization if no driver is assigned. |
380 | if ~HID_DUMP_UNCLAIMED |
380 | if ~HID_DUMP_UNCLAIMED |
381 | cmp [has_driver], 0 |
381 | cmp [has_driver], 0 |
382 | jz got_report.exit |
382 | jz got_report.exit |
383 | end if |
383 | end if |
384 | ; 5. Open interrupt IN pipe. If failed, stop initialization. |
384 | ; 5. Open interrupt IN pipe. If failed, stop initialization. |
385 | mov edx, [ebx+usb_device_data.epdescr] |
385 | mov edx, [ebx+usb_device_data.epdescr] |
386 | movzx ecx, [edx+endpoint_descr.bEndpointAddress] |
386 | movzx ecx, [edx+endpoint_descr.bEndpointAddress] |
387 | movzx eax, [edx+endpoint_descr.bInterval] |
387 | movzx eax, [edx+endpoint_descr.bInterval] |
388 | movzx edx, [edx+endpoint_descr.wMaxPacketSize] |
388 | movzx edx, [edx+endpoint_descr.wMaxPacketSize] |
389 | stdcall USBOpenPipe, [ebx+usb_device_data.configpipe], ecx, edx, INTERRUPT_PIPE, eax |
389 | invoke USBOpenPipe, [ebx+usb_device_data.configpipe], ecx, edx, INTERRUPT_PIPE, eax |
390 | test eax, eax |
390 | test eax, eax |
391 | jz got_report.exit |
391 | jz got_report.exit |
392 | mov [ebx+usb_device_data.intpipe], eax |
392 | mov [ebx+usb_device_data.intpipe], eax |
393 | ; 6. Initialize buffer for input packet. |
393 | ; 6. Initialize buffer for input packet. |
394 | ; 6a. Find the length of input packet. |
394 | ; 6a. Find the length of input packet. |
395 | ; This is the maximal length of all input reports. |
395 | ; This is the maximal length of all input reports. |
396 | mov edx, [ebx+usb_device_data.hid.input.first_report] |
396 | mov edx, [ebx+usb_device_data.hid.input.first_report] |
397 | xor eax, eax |
397 | xor eax, eax |
398 | .find_input_size: |
398 | .find_input_size: |
399 | test edx, edx |
399 | test edx, edx |
400 | jz .found_input_size |
400 | jz .found_input_size |
401 | cmp eax, [edx+report.size] |
401 | cmp eax, [edx+report.size] |
402 | jae @f |
402 | jae @f |
403 | mov eax, [edx+report.size] |
403 | mov eax, [edx+report.size] |
404 | @@: |
404 | @@: |
405 | mov edx, [edx+report.next] |
405 | mov edx, [edx+report.next] |
406 | jmp .find_input_size |
406 | jmp .find_input_size |
407 | .found_input_size: |
407 | .found_input_size: |
408 | ; report.size is in bits, transform it to bytes |
408 | ; report.size is in bits, transform it to bytes |
409 | add eax, 7 |
409 | add eax, 7 |
410 | shr eax, 3 |
410 | shr eax, 3 |
411 | ; if reports are numbered, the first byte is report ID, include it |
411 | ; if reports are numbered, the first byte is report ID, include it |
412 | cmp [ebx+usb_device_data.hid.input.numbered], 0 |
412 | cmp [ebx+usb_device_data.hid.input.numbered], 0 |
413 | jz @f |
413 | jz @f |
414 | inc eax |
414 | inc eax |
415 | @@: |
415 | @@: |
416 | mov [ebx+usb_device_data.input_transfer_size], eax |
416 | mov [ebx+usb_device_data.input_transfer_size], eax |
417 | ; 6b. Allocate memory for input packet: dword-align and add additional dword |
417 | ; 6b. Allocate memory for input packet: dword-align and add additional dword |
418 | ; for extract_field_value. |
418 | ; for extract_field_value. |
419 | add eax, 4+3 |
419 | add eax, 4+3 |
420 | and eax, not 3 |
420 | and eax, not 3 |
421 | call Kmalloc |
421 | invoke Kmalloc |
422 | test eax, eax |
422 | test eax, eax |
423 | jnz @f |
423 | jnz @f |
424 | mov esi, nomemory_msg |
424 | mov esi, nomemory_msg |
425 | call SysMsgBoardStr |
425 | invoke SysMsgBoardStr |
426 | jmp got_report.exit |
426 | jmp got_report.exit |
427 | @@: |
427 | @@: |
428 | mov [ebx+usb_device_data.input_buffer], eax |
428 | mov [ebx+usb_device_data.input_buffer], eax |
429 | ; 7. Submit a request for input packet and wait for input. |
429 | ; 7. Submit a request for input packet and wait for input. |
430 | call ask_for_input |
430 | call ask_for_input |
431 | got_report.exit: |
431 | got_report.exit: |
432 | mov eax, [buffer] |
432 | mov eax, [buffer] |
433 | call Kfree |
433 | invoke Kfree |
434 | ret |
434 | ret |
435 | endp |
435 | endp |
436 | 436 | ||
437 | ; Helper procedure for got_report and got_input. |
437 | ; Helper procedure for got_report and got_input. |
438 | ; Submits a request for the next input packet. |
438 | ; Submits a request for the next input packet. |
439 | proc ask_for_input |
439 | proc ask_for_input |
440 | ; just call USBNormalTransferAsync with correct parameters, |
440 | ; just call USBNormalTransferAsync with correct parameters, |
441 | ; allow short packets |
441 | ; allow short packets |
442 | stdcall USBNormalTransferAsync, \ |
442 | invoke USBNormalTransferAsync, \ |
443 | [ebx+usb_device_data.intpipe], \ |
443 | [ebx+usb_device_data.intpipe], \ |
444 | [ebx+usb_device_data.input_buffer], \ |
444 | [ebx+usb_device_data.input_buffer], \ |
445 | [ebx+usb_device_data.input_transfer_size], \ |
445 | [ebx+usb_device_data.input_transfer_size], \ |
446 | got_input, ebx, \ |
446 | got_input, ebx, \ |
447 | 1 |
447 | 1 |
448 | ret |
448 | ret |
449 | endp |
449 | endp |
450 | 450 | ||
451 | ; This procedure is called by USB stack when a HID device responds with input |
451 | ; This procedure is called by USB stack when a HID device responds with input |
452 | ; data packet. |
452 | ; data packet. |
453 | proc got_input stdcall uses ebx esi edi, pipe, status, buffer, length, calldata |
453 | proc got_input stdcall uses ebx esi edi, pipe, status, buffer, length, calldata |
454 | locals |
454 | locals |
455 | parse_input_locals |
455 | parse_input_locals |
456 | endl |
456 | endl |
457 | ; 1. Validate parameters: fail on error, ignore zero-length transfers. |
457 | ; 1. Validate parameters: fail on error, ignore zero-length transfers. |
458 | mov ebx, [calldata] |
458 | mov ebx, [calldata] |
459 | cmp [status], 0 |
459 | cmp [status], 0 |
460 | jnz .fail |
460 | jnz .fail |
461 | cmp [length], 0 |
461 | cmp [length], 0 |
462 | jz .done |
462 | jz .done |
463 | ; 2. Get pointer to report in esi. |
463 | ; 2. Get pointer to report in esi. |
464 | ; 2a. If there are no report IDs, use hid.input.data. |
464 | ; 2a. If there are no report IDs, use hid.input.data. |
465 | mov eax, [buffer] |
465 | mov eax, [buffer] |
466 | mov esi, [ebx+usb_device_data.hid.input.data] |
466 | mov esi, [ebx+usb_device_data.hid.input.data] |
467 | cmp [ebx+usb_device_data.hid.input.numbered], 0 |
467 | cmp [ebx+usb_device_data.hid.input.numbered], 0 |
468 | jz .report_found |
468 | jz .report_found |
469 | ; 2b. Otherwise, the first byte of report is report ID; |
469 | ; 2b. Otherwise, the first byte of report is report ID; |
470 | ; locate the report by its ID, advance buffer+length to one byte. |
470 | ; locate the report by its ID, advance buffer+length to one byte. |
471 | movzx eax, byte [eax] |
471 | movzx eax, byte [eax] |
472 | mov esi, [esi+eax*4] |
472 | mov esi, [esi+eax*4] |
473 | inc [buffer] |
473 | inc [buffer] |
474 | dec [length] |
474 | dec [length] |
475 | .report_found: |
475 | .report_found: |
476 | ; 3. Validate: ignore transfers with unregistered report IDs |
476 | ; 3. Validate: ignore transfers with unregistered report IDs |
477 | ; and transfers which are too short for the corresponding report. |
477 | ; and transfers which are too short for the corresponding report. |
478 | test esi, esi |
478 | test esi, esi |
479 | jz .done |
479 | jz .done |
480 | mov eax, [esi+report.size] |
480 | mov eax, [esi+report.size] |
481 | add eax, 7 |
481 | add eax, 7 |
482 | shr eax, 3 |
482 | shr eax, 3 |
483 | cmp eax, [length] |
483 | cmp eax, [length] |
484 | ja .done |
484 | ja .done |
485 | ; 4. Pass everything to HID layer. |
485 | ; 4. Pass everything to HID layer. |
486 | parse_input |
486 | parse_input |
487 | .done: |
487 | .done: |
488 | ; 5. Query the next input. |
488 | ; 5. Query the next input. |
489 | mov ebx, [calldata] |
489 | mov ebx, [calldata] |
490 | call ask_for_input |
490 | call ask_for_input |
491 | .nothing: |
491 | .nothing: |
492 | ret |
492 | ret |
493 | .fail: |
493 | .fail: |
494 | mov esi, transfer_error_msg |
494 | mov esi, transfer_error_msg |
495 | call SysMsgBoardStr |
495 | invoke SysMsgBoardStr |
496 | jmp .nothing |
496 | jmp .nothing |
497 | endp |
497 | endp |
498 | 498 | ||
499 | ; This function is called by the USB subsystem when a device is disconnected. |
499 | ; This function is called by the USB subsystem when a device is disconnected. |
500 | proc DeviceDisconnected |
500 | proc DeviceDisconnected |
501 | push ebx esi edi ; save used registers to be stdcall |
501 | push ebx esi edi ; save used registers to be stdcall |
502 | virtual at esp |
502 | virtual at esp |
503 | rd 3 ; saved registers |
503 | rd 3 ; saved registers |
504 | dd ? ; return address |
504 | dd ? ; return address |
505 | .device_data dd ? |
505 | .device_data dd ? |
506 | end virtual |
506 | end virtual |
507 | ; 1. Say a message. |
507 | ; 1. Say a message. |
508 | mov ebx, [.device_data] |
508 | mov ebx, [.device_data] |
509 | mov esi, disconnectmsg |
509 | mov esi, disconnectmsg |
510 | stdcall SysMsgBoardStr |
510 | invoke SysMsgBoardStr |
511 | ; 2. Ask HID layer to release all HID-related resources. |
511 | ; 2. Ask HID layer to release all HID-related resources. |
512 | hid_cleanup |
512 | hid_cleanup |
513 | ; 3. Free the device data. |
513 | ; 3. Free the device data. |
514 | xchg eax, ebx |
514 | xchg eax, ebx |
515 | call Kfree |
515 | invoke Kfree |
516 | ; 4. Return. |
516 | ; 4. Return. |
517 | .nothing: |
517 | .nothing: |
518 | pop edi esi ebx ; restore used registers to be stdcall |
518 | pop edi esi ebx ; restore used registers to be stdcall |
519 | ret 4 ; purge one dword argument to be stdcall |
519 | ret 4 ; purge one dword argument to be stdcall |
520 | endp |
520 | endp |
521 | 521 | ||
522 | include 'sort.inc' |
522 | include 'sort.inc' |
523 | include 'unclaimed.inc' |
523 | include 'unclaimed.inc' |
524 | include 'mouse.inc' |
524 | include 'mouse.inc' |
525 | include 'keyboard.inc' |
525 | include 'keyboard.inc' |
526 | 526 | ||
527 | ; strings |
527 | ; strings |
528 | my_driver db 'usbhid',0 |
528 | my_driver db 'usbhid',0 |
529 | nomemory_msg db 'K : no memory',13,10,0 |
529 | nomemory_msg db 'K : no memory',13,10,0 |
530 | invalid_config_descr_msg db 'K : invalid config descriptor',13,10,0 |
530 | invalid_config_descr_msg db 'K : invalid config descriptor',13,10,0 |
531 | reportfail db 'K : failed to read report descriptor',13,10,0 |
531 | reportfail db 'K : failed to read report descriptor',13,10,0 |
532 | transfer_error_msg db 'K : USB transfer error, disabling HID device',13,10,0 |
532 | transfer_error_msg db 'K : USB transfer error, disabling HID device',13,10,0 |
533 | disconnectmsg db 'K : USB HID device disconnected',13,10,0 |
533 | disconnectmsg db 'K : USB HID device disconnected',13,10,0 |
534 | invalid_report_msg db 'K : report descriptor is invalid',13,10,0 |
534 | invalid_report_msg db 'K : report descriptor is invalid',13,10,0 |
535 | delimiter_note db 'K : note: alternate usage ignored',13,10,0 |
535 | delimiter_note db 'K : note: alternate usage ignored',13,10,0 |
536 | - | ||
537 | ; Exported variable: kernel API version. |
536 | |
538 | align 4 |
- | |
539 | version dd 50005h |
537 | align 4 |
540 | ; Structure with callback functions. |
538 | ; Structure with callback functions. |
541 | usb_functions: |
539 | usb_functions: |
542 | dd 12 |
540 | dd 12 |
543 | dd AddDevice |
541 | dd AddDevice |
544 | dd DeviceDisconnected |
542 | dd DeviceDisconnected |
545 | 543 | ||
546 | ; for DEBUGF macro |
544 | ; for DEBUGF macro |
547 | include_debug_strings |
545 | include_debug_strings |
548 | 546 | ||
549 | ; Workers data |
547 | ; Workers data |
550 | workers_globals |
548 | workers_globals |
- | 549 | ||
551 | 550 | align 4 |
|
552 | ; for uninitialized data |
551 | data fixups |
553 | ;section '.data' data readable writable align 16 |
552 | end data |