Rev 4598 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4598 | Rev 4932 | ||
---|---|---|---|
1 | ; HID keyboard driver, part of USBHID driver. |
1 | ; HID keyboard driver, part of USBHID driver. |
2 | 2 | ||
3 | ; Global constants. |
3 | ; Global constants. |
4 | ; They are assembled in a macro to separate code and data; |
4 | ; They are assembled in a macro to separate code and data; |
5 | ; the code is located at the point of "include 'keyboard.inc'", |
5 | ; the code is located at the point of "include 'keyboard.inc'", |
6 | ; the data are collected when workers_globals is instantiated. |
6 | ; the data are collected when workers_globals is instantiated. |
7 | macro workers_globals |
7 | macro workers_globals |
8 | { |
8 | { |
9 | ; include global constants from previous workers |
9 | ; include global constants from previous workers |
10 | workers_globals |
10 | workers_globals |
11 | align 4 |
11 | align 4 |
12 | ; Callbacks for HID layer. |
12 | ; Callbacks for HID layer. |
13 | keyboard_driver: |
13 | keyboard_driver: |
14 | dd keyboard_driver_add_device |
14 | dd keyboard_driver_add_device |
15 | dd keyboard_driver_disconnect |
15 | dd keyboard_driver_disconnect |
16 | dd keyboard_driver_begin_packet |
16 | dd keyboard_driver_begin_packet |
17 | dd keyboard_driver_array_overflow? |
17 | dd keyboard_driver_array_overflow? |
18 | dd keyboard_driver_input_field |
18 | dd keyboard_driver_input_field |
19 | dd keyboard_driver_end_packet |
19 | dd keyboard_driver_end_packet |
20 | ; Callbacks for keyboard layer. |
20 | ; Callbacks for keyboard layer. |
21 | kbd_functions: |
21 | kbd_functions: |
22 | dd 12 |
22 | dd 12 |
23 | dd CloseKeyboard |
23 | dd CloseKeyboard |
24 | dd SetKeyboardLights |
24 | dd SetKeyboardLights |
25 | ; Kernel keyboard layer takes input in form of PS/2 scancodes. |
25 | ; Kernel keyboard layer takes input in form of PS/2 scancodes. |
26 | ; data for keyboard: correspondence between HID usage keys and PS/2 scancodes. |
26 | ; data for keyboard: correspondence between HID usage keys and PS/2 scancodes. |
27 | EX = 80h ; if set, precede the scancode with special scancode 0xE0 |
27 | EX = 80h ; if set, precede the scancode with special scancode 0xE0 |
28 | label control_keys byte |
28 | label control_keys byte |
29 | ; Usages 700E0h ... 700E7h: LCtrl, LShift, LAlt, LWin, RCtrl, RShift, RAlt, RWin |
29 | ; Usages 700E0h ... 700E7h: LCtrl, LShift, LAlt, LWin, RCtrl, RShift, RAlt, RWin |
30 | db 1Dh, 2Ah, 38h, 5Bh+EX, 1Dh+EX, 36h, 38h+EX, 5Ch+EX |
30 | db 1Dh, 2Ah, 38h, 5Bh+EX, 1Dh+EX, 36h, 38h+EX, 5Ch+EX |
31 | ; Usages 70004h ... 70004h + normal_keys_number - 1 |
31 | ; Usages 70004h ... 70004h + normal_keys_number - 1 |
32 | label normal_keys byte |
32 | label normal_keys byte |
33 | db 1Eh, 30h, 2Eh, 20h, 12h, 21h, 22h, 23h, 17h, 24h, 25h, 26h, 32h, 31h, 18h, 19h |
33 | db 1Eh, 30h, 2Eh, 20h, 12h, 21h, 22h, 23h, 17h, 24h, 25h, 26h, 32h, 31h, 18h, 19h |
34 | db 10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h, 04h, 05h, 06h, 07h |
34 | db 10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h, 04h, 05h, 06h, 07h |
35 | db 08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah, 1Bh, 2Bh, 2Bh, 27h |
35 | db 08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah, 1Bh, 2Bh, 2Bh, 27h |
36 | db 28h, 29h, 33h, 34h, 35h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h, 41h, 42h, 43h, 44h |
36 | db 28h, 29h, 33h, 34h, 35h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h, 41h, 42h, 43h, 44h |
37 | db 57h, 58h,37h+EX,46h,0,52h+EX,47h+EX,49h+EX,53h+EX,4Fh+EX,51h+EX,4Dh+EX,4Bh+EX,50h+EX,48h+EX,45h |
37 | db 57h, 58h,37h+EX,46h,0,52h+EX,47h+EX,49h+EX,53h+EX,4Fh+EX,51h+EX,4Dh+EX,4Bh+EX,50h+EX,48h+EX,45h |
38 | db 35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h, 51h, 4Bh, 4Ch, 4Dh, 47h, 48h, 49h, 52h, 53h |
38 | db 35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h, 51h, 4Bh, 4Ch, 4Dh, 47h, 48h, 49h, 52h, 53h |
39 | db 0,5Dh+EX,5Eh+EX |
39 | db 56h,5Dh+EX,5Eh+EX |
40 | normal_keys_number = $ - normal_keys |
40 | normal_keys_number = $ - normal_keys |
41 | } |
41 | } |
42 | 42 | ||
43 | ; Data that are specific for one keyboard device. |
43 | ; Data that are specific for one keyboard device. |
44 | struct keyboard_device_data |
44 | struct keyboard_device_data |
45 | handle dd ? ; keyboard handle from RegKeyboard |
45 | handle dd ? ; keyboard handle from RegKeyboard |
46 | timer dd ? ; auto-repeat timer handle |
46 | timer dd ? ; auto-repeat timer handle |
47 | repeatkey db ? ; auto-repeat key code |
47 | repeatkey db ? ; auto-repeat key code |
48 | rb 3 ; padding |
48 | rb 3 ; padding |
49 | usbdev dd ? ; pointer to device_data of USB and HID layers |
49 | usbdev dd ? ; pointer to device_data of USB and HID layers |
50 | modifiers dd ? ; state of LCtrl ... RWin |
50 | modifiers dd ? ; state of LCtrl ... RWin |
51 | led_report dd ? ; output report for LEDs state |
51 | led_report dd ? ; output report for LEDs state |
52 | numlock_bit dd ? ; position of NumLock bit in LED output report |
52 | numlock_bit dd ? ; position of NumLock bit in LED output report |
53 | capslock_bit dd ? |
53 | capslock_bit dd ? |
54 | scrolllock_bit dd ? ; guess what |
54 | scrolllock_bit dd ? ; guess what |
55 | ends |
55 | ends |
56 | 56 | ||
57 | ; This procedure is called when HID layer detects a new keyboard. |
57 | ; This procedure is called when HID layer detects a new keyboard. |
58 | ; in: ebx -> usb_device_data, edi -> collection |
58 | ; in: ebx -> usb_device_data, edi -> collection |
59 | ; out: eax = device-specific data or NULL on error |
59 | ; out: eax = device-specific data or NULL on error |
60 | proc keyboard_driver_add_device |
60 | proc keyboard_driver_add_device |
61 | ; 1. Allocate memory for keyboard_device_data. If failed, return NULL. |
61 | ; 1. Allocate memory for keyboard_device_data. If failed, return NULL. |
62 | movi eax, sizeof.keyboard_device_data |
62 | movi eax, sizeof.keyboard_device_data |
63 | call Kmalloc |
63 | call Kmalloc |
64 | test eax, eax |
64 | test eax, eax |
65 | jz .nothing |
65 | jz .nothing |
66 | ; 2. Initialize keyboard_device_data: store pointer to USB layer data, |
66 | ; 2. Initialize keyboard_device_data: store pointer to USB layer data, |
67 | ; zero some fields, initialize bit positions to -1. |
67 | ; zero some fields, initialize bit positions to -1. |
68 | mov [eax+keyboard_device_data.usbdev], ebx |
68 | mov [eax+keyboard_device_data.usbdev], ebx |
69 | xor ecx, ecx |
69 | xor ecx, ecx |
70 | mov [eax+keyboard_device_data.timer], ecx |
70 | mov [eax+keyboard_device_data.timer], ecx |
71 | mov [eax+keyboard_device_data.repeatkey], cl |
71 | mov [eax+keyboard_device_data.repeatkey], cl |
72 | mov [eax+keyboard_device_data.modifiers], ecx |
72 | mov [eax+keyboard_device_data.modifiers], ecx |
73 | mov [eax+keyboard_device_data.led_report], ecx |
73 | mov [eax+keyboard_device_data.led_report], ecx |
74 | dec ecx |
74 | dec ecx |
75 | mov [eax+keyboard_device_data.numlock_bit], ecx |
75 | mov [eax+keyboard_device_data.numlock_bit], ecx |
76 | mov [eax+keyboard_device_data.capslock_bit], ecx |
76 | mov [eax+keyboard_device_data.capslock_bit], ecx |
77 | mov [eax+keyboard_device_data.scrolllock_bit], ecx |
77 | mov [eax+keyboard_device_data.scrolllock_bit], ecx |
78 | ; 3. Look for LED report and bits corresponding to indicators. |
78 | ; 3. Look for LED report and bits corresponding to indicators. |
79 | ; For now, assume that all LEDs are set by the same report. |
79 | ; For now, assume that all LEDs are set by the same report. |
80 | ; 3a. Save registers. |
80 | ; 3a. Save registers. |
81 | push ebx esi |
81 | push ebx esi |
82 | ; 3b. Prepare for loop over output reports: get the first output report. |
82 | ; 3b. Prepare for loop over output reports: get the first output report. |
83 | ; If there are no output records, skip step 3; |
83 | ; If there are no output records, skip step 3; |
84 | ; default values of led_report and *_bit were set in step 2. |
84 | ; default values of led_report and *_bit were set in step 2. |
85 | mov edx, [edi+collection.output.first_report] |
85 | mov edx, [edi+collection.output.first_report] |
86 | test edx, edx |
86 | test edx, edx |
87 | jz .led_report_set |
87 | jz .led_report_set |
88 | .scan_led_report: |
88 | .scan_led_report: |
89 | ; Process one output report. |
89 | ; Process one output report. |
90 | ; 3c. Prepare for loop over field groups in the current report: |
90 | ; 3c. Prepare for loop over field groups in the current report: |
91 | ; get the first field group. |
91 | ; get the first field group. |
92 | mov ecx, [edx+report.first_field] |
92 | mov ecx, [edx+report.first_field] |
93 | .scan_led_field: |
93 | .scan_led_field: |
94 | ; Process one field group. |
94 | ; Process one field group. |
95 | ; 3d. If there are no more field groups, exit the loop over field groups. |
95 | ; 3d. If there are no more field groups, exit the loop over field groups. |
96 | test ecx, ecx |
96 | test ecx, ecx |
97 | jz .next_led_report |
97 | jz .next_led_report |
98 | ; For now, assume that all LEDs are plain variable fields, not arrays. |
98 | ; For now, assume that all LEDs are plain variable fields, not arrays. |
99 | ; 3e. Ignore array field groups. |
99 | ; 3e. Ignore array field groups. |
100 | test byte [ecx+report_field_group.flags], HID_FIELD_VARIABLE |
100 | test byte [ecx+report_field_group.flags], HID_FIELD_VARIABLE |
101 | jz .next_led_field |
101 | jz .next_led_field |
102 | ; 3f. Loop over all fields in the current group. |
102 | ; 3f. Loop over all fields in the current group. |
103 | push [ecx+report_field_group.count] |
103 | push [ecx+report_field_group.count] |
104 | ; esi = pointer to usage of the current field |
104 | ; esi = pointer to usage of the current field |
105 | lea esi, [ecx+report_field_group.common_sizeof] |
105 | lea esi, [ecx+report_field_group.common_sizeof] |
106 | ; ebx = bit position of the current field |
106 | ; ebx = bit position of the current field |
107 | mov ebx, [ecx+report_field_group.offset] |
107 | mov ebx, [ecx+report_field_group.offset] |
108 | ; if report is numbered, add extra byte in the start of report |
108 | ; if report is numbered, add extra byte in the start of report |
109 | cmp [edx+report.id], 0 |
109 | cmp [edx+report.id], 0 |
110 | jz .scan_led_usage |
110 | jz .scan_led_usage |
111 | add ebx, 8 |
111 | add ebx, 8 |
112 | .scan_led_usage: |
112 | .scan_led_usage: |
113 | ; for USAGE_LED_*LOCK, store the current bit position in the corresponding field |
113 | ; for USAGE_LED_*LOCK, store the current bit position in the corresponding field |
114 | ; and store the current report as the LED report |
114 | ; and store the current report as the LED report |
115 | cmp dword [esi], USAGE_LED_NUMLOCK |
115 | cmp dword [esi], USAGE_LED_NUMLOCK |
116 | jz .numlock |
116 | jz .numlock |
117 | cmp dword [esi], USAGE_LED_CAPSLOCK |
117 | cmp dword [esi], USAGE_LED_CAPSLOCK |
118 | jz .capslock |
118 | jz .capslock |
119 | cmp dword [esi], USAGE_LED_SCROLLLOCK |
119 | cmp dword [esi], USAGE_LED_SCROLLLOCK |
120 | jnz .next_field |
120 | jnz .next_field |
121 | .scrolllock: |
121 | .scrolllock: |
122 | mov [eax+keyboard_device_data.scrolllock_bit], ebx |
122 | mov [eax+keyboard_device_data.scrolllock_bit], ebx |
123 | jmp @f |
123 | jmp @f |
124 | .capslock: |
124 | .capslock: |
125 | mov [eax+keyboard_device_data.capslock_bit], ebx |
125 | mov [eax+keyboard_device_data.capslock_bit], ebx |
126 | jmp @f |
126 | jmp @f |
127 | .numlock: |
127 | .numlock: |
128 | mov [eax+keyboard_device_data.numlock_bit], ebx |
128 | mov [eax+keyboard_device_data.numlock_bit], ebx |
129 | @@: |
129 | @@: |
130 | mov [eax+keyboard_device_data.led_report], edx |
130 | mov [eax+keyboard_device_data.led_report], edx |
131 | .next_field: |
131 | .next_field: |
132 | add esi, 4 |
132 | add esi, 4 |
133 | add ebx, [ecx+report_field_group.size] |
133 | add ebx, [ecx+report_field_group.size] |
134 | dec dword [esp] |
134 | dec dword [esp] |
135 | jnz .scan_led_usage |
135 | jnz .scan_led_usage |
136 | pop ebx |
136 | pop ebx |
137 | .next_led_field: |
137 | .next_led_field: |
138 | ; 3g. Continue loop over field groups: get next field group. |
138 | ; 3g. Continue loop over field groups: get next field group. |
139 | mov ecx, [ecx+report_field_group.next] |
139 | mov ecx, [ecx+report_field_group.next] |
140 | jmp .scan_led_field |
140 | jmp .scan_led_field |
141 | .next_led_report: |
141 | .next_led_report: |
142 | ; 3h. If the LED report has been set, break from the loop over reports. |
142 | ; 3h. If the LED report has been set, break from the loop over reports. |
143 | ; Otherwise, get the next report and continue if the current report is not |
143 | ; Otherwise, get the next report and continue if the current report is not |
144 | ; the last for this collection. |
144 | ; the last for this collection. |
145 | cmp [eax+keyboard_device_data.led_report], 0 |
145 | cmp [eax+keyboard_device_data.led_report], 0 |
146 | jnz .led_report_set |
146 | jnz .led_report_set |
147 | cmp edx, [edi+collection.output.last_report] |
147 | cmp edx, [edi+collection.output.last_report] |
148 | mov edx, [edx+report.next] |
148 | mov edx, [edx+report.next] |
149 | jnz .scan_led_report |
149 | jnz .scan_led_report |
150 | .led_report_set: |
150 | .led_report_set: |
151 | ; 3i. Restore registers. |
151 | ; 3i. Restore registers. |
152 | pop esi ebx |
152 | pop esi ebx |
153 | ; 4. Register keyboard in the kernel. |
153 | ; 4. Register keyboard in the kernel. |
154 | ; store pointer to keyboard_device_data in the stack |
154 | ; store pointer to keyboard_device_data in the stack |
155 | push eax |
155 | push eax |
156 | ; call kernel API |
156 | ; call kernel API |
157 | stdcall RegKeyboard, kbd_functions, eax |
157 | stdcall RegKeyboard, kbd_functions, eax |
158 | ; restore pointer to keyboard_device_data from the stack, |
158 | ; restore pointer to keyboard_device_data from the stack, |
159 | ; putting keyboard handle from API to the stack |
159 | ; putting keyboard handle from API to the stack |
160 | xchg eax, [esp] |
160 | xchg eax, [esp] |
161 | ; put keyboard handle from API from the stack to keyboard_device_data field |
161 | ; put keyboard handle from API from the stack to keyboard_device_data field |
162 | pop [eax+keyboard_device_data.handle] |
162 | pop [eax+keyboard_device_data.handle] |
163 | ; If failed, free keyboard_device_data and return NULL. |
163 | ; If failed, free keyboard_device_data and return NULL. |
164 | cmp [eax+keyboard_device_data.handle], 0 |
164 | cmp [eax+keyboard_device_data.handle], 0 |
165 | jz .fail_free |
165 | jz .fail_free |
166 | ; 5. Return pointer to keyboard_device_data. |
166 | ; 5. Return pointer to keyboard_device_data. |
167 | .nothing: |
167 | .nothing: |
168 | ret |
168 | ret |
169 | .fail_free: |
169 | .fail_free: |
170 | call Kfree |
170 | call Kfree |
171 | xor eax, eax |
171 | xor eax, eax |
172 | ret |
172 | ret |
173 | endp |
173 | endp |
174 | 174 | ||
175 | ; This procedure is called when HID layer detects disconnect of a previously |
175 | ; This procedure is called when HID layer detects disconnect of a previously |
176 | ; connected keyboard. |
176 | ; connected keyboard. |
177 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
177 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
178 | proc keyboard_driver_disconnect |
178 | proc keyboard_driver_disconnect |
179 | ; 1. If an autorepeat timer is active, stop it. |
179 | ; 1. If an autorepeat timer is active, stop it. |
180 | cmp [edi+keyboard_device_data.timer], 0 |
180 | cmp [edi+keyboard_device_data.timer], 0 |
181 | jz @f |
181 | jz @f |
182 | stdcall CancelTimerHS, [edi+keyboard_device_data.timer] |
182 | stdcall CancelTimerHS, [edi+keyboard_device_data.timer] |
183 | @@: |
183 | @@: |
184 | ; 2. Unregister keyboard in the kernel. |
184 | ; 2. Unregister keyboard in the kernel. |
185 | stdcall DelKeyboard, [edi+keyboard_device_data.handle] |
185 | stdcall DelKeyboard, [edi+keyboard_device_data.handle] |
186 | ; We should free data in CloseKeyboard, not here. |
186 | ; We should free data in CloseKeyboard, not here. |
187 | ret |
187 | ret |
188 | endp |
188 | endp |
189 | 189 | ||
190 | ; This procedure is called when HID layer starts processing a new input packet |
190 | ; This procedure is called when HID layer starts processing a new input packet |
191 | ; from a keyboard. |
191 | ; from a keyboard. |
192 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
192 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
193 | proc keyboard_driver_begin_packet |
193 | proc keyboard_driver_begin_packet |
194 | ; Nothing to do. |
194 | ; Nothing to do. |
195 | ret |
195 | ret |
196 | endp |
196 | endp |
197 | 197 | ||
198 | ; This procedure is called when HID layer processes every non-empty array field group. |
198 | ; This procedure is called when HID layer processes every non-empty array field group. |
199 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
199 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
200 | ; in: ecx = fields count (always nonzero), edx = pointer to fields values |
200 | ; in: ecx = fields count (always nonzero), edx = pointer to fields values |
201 | ; in: esi -> report_field_group |
201 | ; in: esi -> report_field_group |
202 | ; out: CF set => group is ok, CF cleared => group should be ignored |
202 | ; out: CF set => group is ok, CF cleared => group should be ignored |
203 | proc keyboard_driver_array_overflow? |
203 | proc keyboard_driver_array_overflow? |
204 | ; The keyboard signals array overflow by filling the entire array with |
204 | ; The keyboard signals array overflow by filling the entire array with |
205 | ; USAGE_KBD_ROLLOVER codes. |
205 | ; USAGE_KBD_ROLLOVER codes. |
206 | mov eax, [edx] ; eax = first field in the array |
206 | mov eax, [edx] ; eax = first field in the array |
207 | sub eax, USAGE_KBD_ROLLOVER ; eax = 0 if overflow, nonzero otherwise |
207 | sub eax, USAGE_KBD_ROLLOVER ; eax = 0 if overflow, nonzero otherwise |
208 | neg eax ; CF cleared if eax was zero, CF set if eax was nonzero |
208 | neg eax ; CF cleared if eax was zero, CF set if eax was nonzero |
209 | ret |
209 | ret |
210 | endp |
210 | endp |
211 | 211 | ||
212 | ; This procedure is called from HID layer for every field. |
212 | ; This procedure is called from HID layer for every field. |
213 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
213 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
214 | ; in: ecx = field usage, edx = value, esi -> report_field_group |
214 | ; in: ecx = field usage, edx = value, esi -> report_field_group |
215 | proc keyboard_driver_input_field |
215 | proc keyboard_driver_input_field |
216 | if HID_DUMP_UNCLAIMED |
216 | if HID_DUMP_UNCLAIMED |
217 | .unclaimed = default_driver_input_field |
217 | .unclaimed = default_driver_input_field |
218 | end if |
218 | end if |
219 | ; 1. Process normal keys: |
219 | ; 1. Process normal keys: |
220 | ; from USAGE_KBD_FIRST_KEY to USAGE_KBD_FIRST_KEY + normal_keys_number - 1, |
220 | ; from USAGE_KBD_FIRST_KEY to USAGE_KBD_FIRST_KEY + normal_keys_number - 1, |
221 | ; excluding zeroes in [normal_keys]. |
221 | ; excluding zeroes in [normal_keys]. |
222 | ; 1a. Test whether usage is in the range. |
222 | ; 1a. Test whether usage is in the range. |
223 | lea eax, [ecx-USAGE_KBD_FIRST_KEY] |
223 | lea eax, [ecx-USAGE_KBD_FIRST_KEY] |
224 | cmp eax, normal_keys_number |
224 | cmp eax, normal_keys_number |
225 | jae .not_normal_key |
225 | jae .not_normal_key |
226 | ; 1b. If the corresponding entry in [normal_keys] is zero, |
226 | ; 1b. If the corresponding entry in [normal_keys] is zero, |
227 | ; pass this field to the default handler - if HID_DUMP_UNCLAIMED is enabled, |
227 | ; pass this field to the default handler - if HID_DUMP_UNCLAIMED is enabled, |
228 | ; default handler is default_driver_input_field, otherwise just ignore the field. |
228 | ; default handler is default_driver_input_field, otherwise just ignore the field. |
229 | cmp [normal_keys + eax], 0 |
229 | cmp [normal_keys + eax], 0 |
230 | jz .unclaimed |
230 | jz .unclaimed |
231 | ; 1c. Get the scancode. |
231 | ; 1c. Get the scancode. |
232 | movzx ecx, [normal_keys + eax] |
232 | movzx ecx, [normal_keys + eax] |
233 | ; 1d. Further actions are slightly different for key press and key release. |
233 | ; 1d. Further actions are slightly different for key press and key release. |
234 | ; Decide what to do. |
234 | ; Decide what to do. |
235 | test edx, edx |
235 | test edx, edx |
236 | jz .normal_key_released |
236 | jz .normal_key_released |
237 | .normal_key_pressed: |
237 | .normal_key_pressed: |
238 | ; The key is pressed. |
238 | ; The key is pressed. |
239 | ; 1e. Store the last pressed key for autorepeat. |
239 | ; 1e. Store the last pressed key for autorepeat. |
240 | mov [edi+keyboard_device_data.repeatkey], cl |
240 | mov [edi+keyboard_device_data.repeatkey], cl |
241 | ; 1f. Copy bit 7 to CF and send scancode with bit 7 cleared. |
241 | ; 1f. Copy bit 7 to CF and send scancode with bit 7 cleared. |
242 | btr ecx, 7 |
242 | btr ecx, 7 |
243 | call .send_key |
243 | call .send_key |
244 | ; 1g. Stop the previous autorepeat timer, if any. |
244 | ; 1g. Stop the previous autorepeat timer, if any. |
245 | mov eax, [edi+keyboard_device_data.timer] |
245 | mov eax, [edi+keyboard_device_data.timer] |
246 | test eax, eax |
246 | test eax, eax |
247 | jz @f |
247 | jz @f |
248 | stdcall CancelTimerHS, eax |
248 | stdcall CancelTimerHS, eax |
249 | @@: |
249 | @@: |
250 | ; 1h. Start the new autorepeat timer with 250 ms initial delay |
250 | ; 1h. Start the new autorepeat timer with 250 ms initial delay |
251 | ; and 50 ms subsequent delays. |
251 | ; and 50 ms subsequent delays. |
252 | stdcall TimerHS, 25, 5, autorepeat_timer, edi |
252 | stdcall TimerHS, 25, 5, autorepeat_timer, edi |
253 | mov [edi+keyboard_device_data.timer], eax |
253 | mov [edi+keyboard_device_data.timer], eax |
254 | if ~HID_DUMP_UNCLAIMED |
254 | if ~HID_DUMP_UNCLAIMED |
255 | .unclaimed: |
255 | .unclaimed: |
256 | end if |
256 | end if |
257 | ret |
257 | ret |
258 | .normal_key_released: |
258 | .normal_key_released: |
259 | ; The key is released. |
259 | ; The key is released. |
260 | ; 1i. Stop the autorepeat timer if it is autorepeating the released key. |
260 | ; 1i. Stop the autorepeat timer if it is autorepeating the released key. |
261 | cmp [edi+keyboard_device_data.repeatkey], cl |
261 | cmp [edi+keyboard_device_data.repeatkey], cl |
262 | jnz .no_stop_timer |
262 | jnz .no_stop_timer |
263 | push ecx |
263 | push ecx |
264 | mov [edi+keyboard_device_data.repeatkey], 0 |
264 | mov [edi+keyboard_device_data.repeatkey], 0 |
265 | mov eax, [edi+keyboard_device_data.timer] |
265 | mov eax, [edi+keyboard_device_data.timer] |
266 | test eax, eax |
266 | test eax, eax |
267 | jz @f |
267 | jz @f |
268 | stdcall CancelTimerHS, eax |
268 | stdcall CancelTimerHS, eax |
269 | mov [edi+keyboard_device_data.timer], 0 |
269 | mov [edi+keyboard_device_data.timer], 0 |
270 | @@: |
270 | @@: |
271 | pop ecx |
271 | pop ecx |
272 | .no_stop_timer: |
272 | .no_stop_timer: |
273 | ; 1j. Copy bit 7 to CF and send scancode with bit 7 set. |
273 | ; 1j. Copy bit 7 to CF and send scancode with bit 7 set. |
274 | bts ecx, 7 |
274 | bts ecx, 7 |
275 | call .send_key |
275 | call .send_key |
276 | ret |
276 | ret |
277 | .not_normal_key: |
277 | .not_normal_key: |
278 | ; 2. USAGE_KBD_NOEVENT is simply a filler for free array fields, |
278 | ; 2. USAGE_KBD_NOEVENT is simply a filler for free array fields, |
279 | ; ignore it. |
279 | ; ignore it. |
280 | cmp ecx, USAGE_KBD_NOEVENT |
280 | cmp ecx, USAGE_KBD_NOEVENT |
281 | jz .nothing |
281 | jz .nothing |
282 | ; 3. Process modifiers: 8 keys starting at USAGE_KBD_LCTRL. |
282 | ; 3. Process modifiers: 8 keys starting at USAGE_KBD_LCTRL. |
283 | ; 3a. Test whether usage is in range. |
283 | ; 3a. Test whether usage is in range. |
284 | ; If not, we don't know what this field means, so pass it to the default handler. |
284 | ; If not, we don't know what this field means, so pass it to the default handler. |
285 | lea eax, [ecx-USAGE_KBD_LCTRL] |
285 | lea eax, [ecx-USAGE_KBD_LCTRL] |
286 | cmp eax, 8 |
286 | cmp eax, 8 |
287 | jae .unclaimed |
287 | jae .unclaimed |
288 | ; 3b. Further actions are slightly different for modifier press |
288 | ; 3b. Further actions are slightly different for modifier press |
289 | ; and modifier release. Decide what to do. |
289 | ; and modifier release. Decide what to do. |
290 | test edx, edx |
290 | test edx, edx |
291 | jz .modifier_not_pressed |
291 | jz .modifier_not_pressed |
292 | .modifier_pressed: |
292 | .modifier_pressed: |
293 | ; The modifier is pressed. |
293 | ; The modifier is pressed. |
294 | ; 3c. Set the corresponding status bit. |
294 | ; 3c. Set the corresponding status bit. |
295 | ; If it was not set, send the corresponding scancode to the kernel |
295 | ; If it was not set, send the corresponding scancode to the kernel |
296 | ; with bit 7 cleared. |
296 | ; with bit 7 cleared. |
297 | bts [edi+keyboard_device_data.modifiers], eax |
297 | bts [edi+keyboard_device_data.modifiers], eax |
298 | jc @f |
298 | jc @f |
299 | movzx ecx, [control_keys+eax] |
299 | movzx ecx, [control_keys+eax] |
300 | btr ecx, 7 |
300 | btr ecx, 7 |
301 | call .send_key |
301 | call .send_key |
302 | @@: |
302 | @@: |
303 | .nothing: |
303 | .nothing: |
304 | ret |
304 | ret |
305 | .modifier_not_pressed: |
305 | .modifier_not_pressed: |
306 | ; The modifier is not pressed. |
306 | ; The modifier is not pressed. |
307 | ; 3d. Clear the correspodning status bit. |
307 | ; 3d. Clear the correspodning status bit. |
308 | ; If it was set, send the corresponding scancode to the kernel |
308 | ; If it was set, send the corresponding scancode to the kernel |
309 | ; with bit 7 set. |
309 | ; with bit 7 set. |
310 | btr [edi+keyboard_device_data.modifiers], eax |
310 | btr [edi+keyboard_device_data.modifiers], eax |
311 | jnc @f |
311 | jnc @f |
312 | movzx ecx, [control_keys+eax] |
312 | movzx ecx, [control_keys+eax] |
313 | bts ecx, 7 |
313 | bts ecx, 7 |
314 | call .send_key |
314 | call .send_key |
315 | @@: |
315 | @@: |
316 | ret |
316 | ret |
317 | 317 | ||
318 | ; Helper procedure. Sends scancode from cl to the kernel. |
318 | ; Helper procedure. Sends scancode from cl to the kernel. |
319 | ; If CF is set, precede it with special code 0xE0. |
319 | ; If CF is set, precede it with special code 0xE0. |
320 | .send_key: |
320 | .send_key: |
321 | jnc @f |
321 | jnc @f |
322 | push ecx |
322 | push ecx |
323 | mov ecx, 0xE0 |
323 | mov ecx, 0xE0 |
324 | call SetKeyboardData |
324 | call SetKeyboardData |
325 | pop ecx |
325 | pop ecx |
326 | @@: |
326 | @@: |
327 | call SetKeyboardData |
327 | call SetKeyboardData |
328 | ret |
328 | ret |
329 | endp |
329 | endp |
330 | 330 | ||
331 | ; This procedure is called when HID layer ends processing a new input packet |
331 | ; This procedure is called when HID layer ends processing a new input packet |
332 | ; from a keyboard. |
332 | ; from a keyboard. |
333 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
333 | ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device) |
334 | proc keyboard_driver_end_packet |
334 | proc keyboard_driver_end_packet |
335 | ; Nothing to do. |
335 | ; Nothing to do. |
336 | ret |
336 | ret |
337 | endp |
337 | endp |
338 | 338 | ||
339 | ; Timer callback for SetTimerHS. |
339 | ; Timer callback for SetTimerHS. |
340 | proc autorepeat_timer |
340 | proc autorepeat_timer |
341 | virtual at esp |
341 | virtual at esp |
342 | dd ? ; return address |
342 | dd ? ; return address |
343 | .data dd ? |
343 | .data dd ? |
344 | end virtual |
344 | end virtual |
345 | ; Just resend the last pressed key. |
345 | ; Just resend the last pressed key. |
346 | mov eax, [.data] |
346 | mov eax, [.data] |
347 | movzx ecx, [eax+keyboard_device_data.repeatkey] |
347 | movzx ecx, [eax+keyboard_device_data.repeatkey] |
348 | ; Copy bit 7 to CF and send scancode with bit 7 cleared. |
348 | ; Copy bit 7 to CF and send scancode with bit 7 cleared. |
349 | btr ecx, 7 |
349 | btr ecx, 7 |
350 | call keyboard_driver_input_field.send_key |
350 | call keyboard_driver_input_field.send_key |
351 | ret 4 |
351 | ret 4 |
352 | endp |
352 | endp |
353 | 353 | ||
354 | ; This function is called from the keyboard layer |
354 | ; This function is called from the keyboard layer |
355 | ; when it is safe to free keyboard data. |
355 | ; when it is safe to free keyboard data. |
356 | proc CloseKeyboard |
356 | proc CloseKeyboard |
357 | virtual at esp |
357 | virtual at esp |
358 | dd ? ; return address |
358 | dd ? ; return address |
359 | .device_data dd ? |
359 | .device_data dd ? |
360 | end virtual |
360 | end virtual |
361 | mov eax, [.device_data] |
361 | mov eax, [.device_data] |
362 | call Kfree |
362 | call Kfree |
363 | ret 4 |
363 | ret 4 |
364 | endp |
364 | endp |
365 | 365 | ||
366 | ; This function is called from the keyboard layer |
366 | ; This function is called from the keyboard layer |
367 | ; to update LED state on the keyboard. |
367 | ; to update LED state on the keyboard. |
368 | proc SetKeyboardLights stdcall uses ebx esi edi, device_data, led_state |
368 | proc SetKeyboardLights stdcall uses ebx esi edi, device_data, led_state |
369 | locals |
369 | locals |
370 | size dd ? |
370 | size dd ? |
371 | endl |
371 | endl |
372 | ; 1. Get the pointer to the LED report. |
372 | ; 1. Get the pointer to the LED report. |
373 | ; If there is no LED report, exit from the function. |
373 | ; If there is no LED report, exit from the function. |
374 | mov ebx, [device_data] |
374 | mov ebx, [device_data] |
375 | mov esi, [ebx+keyboard_device_data.led_report] |
375 | mov esi, [ebx+keyboard_device_data.led_report] |
376 | test esi, esi |
376 | test esi, esi |
377 | jz .nothing |
377 | jz .nothing |
378 | ; 2. Get report size in bytes. |
378 | ; 2. Get report size in bytes. |
379 | ; report.size is size in bits without possible report ID; |
379 | ; report.size is size in bits without possible report ID; |
380 | ; if an ID is assigned, the size is one byte greater. |
380 | ; if an ID is assigned, the size is one byte greater. |
381 | mov eax, [esi+report.size] |
381 | mov eax, [esi+report.size] |
382 | add eax, 7 |
382 | add eax, 7 |
383 | shr eax, 3 |
383 | shr eax, 3 |
384 | cmp [esi+report.id], 0 |
384 | cmp [esi+report.id], 0 |
385 | jz @f |
385 | jz @f |
386 | inc eax |
386 | inc eax |
387 | @@: |
387 | @@: |
388 | mov [size], eax |
388 | mov [size], eax |
389 | ; 3. Allocate memory for report + 8 bytes for setup packet. |
389 | ; 3. Allocate memory for report + 8 bytes for setup packet. |
390 | ; Dword-align size for subsequent rep stosd and bts. |
390 | ; Dword-align size for subsequent rep stosd and bts. |
391 | ; If failed, exit from the function. |
391 | ; If failed, exit from the function. |
392 | add eax, 8 + 3 |
392 | add eax, 8 + 3 |
393 | and eax, not 3 |
393 | and eax, not 3 |
394 | push eax |
394 | push eax |
395 | call Kmalloc |
395 | call Kmalloc |
396 | pop ecx |
396 | pop ecx |
397 | test eax, eax |
397 | test eax, eax |
398 | jz .nothing |
398 | jz .nothing |
399 | ; 4. Zero-initialize output report. |
399 | ; 4. Zero-initialize output report. |
400 | push eax |
400 | push eax |
401 | mov edi, eax |
401 | mov edi, eax |
402 | shr ecx, 2 |
402 | shr ecx, 2 |
403 | xor eax, eax |
403 | xor eax, eax |
404 | rep stosd |
404 | rep stosd |
405 | pop edi |
405 | pop edi |
406 | add edi, 8 |
406 | add edi, 8 |
407 | ; 5. Store report ID, if assigned. If not assigned, that would just write zero |
407 | ; 5. Store report ID, if assigned. If not assigned, that would just write zero |
408 | ; over zeroes. |
408 | ; over zeroes. |
409 | mov edx, [esi+report.id] |
409 | mov edx, [esi+report.id] |
410 | mov [edi], edx |
410 | mov [edi], edx |
411 | ; 6. Set report bits corresponding to active indicators. |
411 | ; 6. Set report bits corresponding to active indicators. |
412 | mov eax, [led_state] |
412 | mov eax, [led_state] |
413 | test al, 1 ; PS/2 Scroll Lock |
413 | test al, 1 ; PS/2 Scroll Lock |
414 | jz @f |
414 | jz @f |
415 | mov ecx, [ebx+keyboard_device_data.scrolllock_bit] |
415 | mov ecx, [ebx+keyboard_device_data.scrolllock_bit] |
416 | test ecx, ecx |
416 | test ecx, ecx |
417 | js @f |
417 | js @f |
418 | bts [edi], ecx |
418 | bts [edi], ecx |
419 | @@: |
419 | @@: |
420 | test al, 2 ; PS/2 Num Lock |
420 | test al, 2 ; PS/2 Num Lock |
421 | jz @f |
421 | jz @f |
422 | mov ecx, [ebx+keyboard_device_data.numlock_bit] |
422 | mov ecx, [ebx+keyboard_device_data.numlock_bit] |
423 | test ecx, ecx |
423 | test ecx, ecx |
424 | js @f |
424 | js @f |
425 | bts [edi], ecx |
425 | bts [edi], ecx |
426 | @@: |
426 | @@: |
427 | test al, 4 ; PS/2 Caps Lock |
427 | test al, 4 ; PS/2 Caps Lock |
428 | jz @f |
428 | jz @f |
429 | mov ecx, [ebx+keyboard_device_data.capslock_bit] |
429 | mov ecx, [ebx+keyboard_device_data.capslock_bit] |
430 | test ecx, ecx |
430 | test ecx, ecx |
431 | js @f |
431 | js @f |
432 | bts [edi], ecx |
432 | bts [edi], ecx |
433 | @@: |
433 | @@: |
434 | ; 7. Fill setup packet. |
434 | ; 7. Fill setup packet. |
435 | shl edx, 16 ; move Report ID to byte 2 |
435 | shl edx, 16 ; move Report ID to byte 2 |
436 | or edx, 21h + \ ; Class-specific request to Interface |
436 | or edx, 21h + \ ; Class-specific request to Interface |
437 | (9 shl 8) + \ ; SET_REPORT |
437 | (9 shl 8) + \ ; SET_REPORT |
438 | (2 shl 24) ; Report Type = Output |
438 | (2 shl 24) ; Report Type = Output |
439 | lea eax, [edi-8] |
439 | lea eax, [edi-8] |
440 | mov ebx, [ebx+keyboard_device_data.usbdev] |
440 | mov ebx, [ebx+keyboard_device_data.usbdev] |
441 | mov dword [eax], edx |
441 | mov dword [eax], edx |
442 | mov edx, [size] |
442 | mov edx, [size] |
443 | shl edx, 16 ; move Size to last word |
443 | shl edx, 16 ; move Size to last word |
444 | or edx, [ebx+usb_device_data.interface_number] |
444 | or edx, [ebx+usb_device_data.interface_number] |
445 | mov [eax+4], edx |
445 | mov [eax+4], edx |
446 | ; 8. Submit output control request. |
446 | ; 8. Submit output control request. |
447 | stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \ |
447 | stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \ |
448 | eax, edi, [size], after_set_keyboard_lights, ebx, 0 |
448 | eax, edi, [size], after_set_keyboard_lights, ebx, 0 |
449 | ; If failed, free the buffer now. |
449 | ; If failed, free the buffer now. |
450 | ; If succeeded, the callback will free the buffer. |
450 | ; If succeeded, the callback will free the buffer. |
451 | test eax, eax |
451 | test eax, eax |
452 | jnz .nothing |
452 | jnz .nothing |
453 | lea eax, [edi-8] |
453 | lea eax, [edi-8] |
454 | call Kfree |
454 | call Kfree |
455 | .nothing: |
455 | .nothing: |
456 | ret |
456 | ret |
457 | endp |
457 | endp |
458 | 458 | ||
459 | ; This procedure is called from the USB subsystem when the request initiated by |
459 | ; This procedure is called from the USB subsystem when the request initiated by |
460 | ; SetKeyboardLights is completed, either successfully or unsuccessfully. |
460 | ; SetKeyboardLights is completed, either successfully or unsuccessfully. |
461 | proc after_set_keyboard_lights |
461 | proc after_set_keyboard_lights |
462 | virtual at esp |
462 | virtual at esp |
463 | dd ? ; return address |
463 | dd ? ; return address |
464 | .pipe dd ? |
464 | .pipe dd ? |
465 | .status dd ? |
465 | .status dd ? |
466 | .buffer dd ? |
466 | .buffer dd ? |
467 | .length dd ? |
467 | .length dd ? |
468 | .calldata dd ? |
468 | .calldata dd ? |
469 | end virtual |
469 | end virtual |
470 | ; Ignore status, just free the buffer allocated by SetKeyboardLights. |
470 | ; Ignore status, just free the buffer allocated by SetKeyboardLights. |
471 | mov eax, [.buffer] |
471 | mov eax, [.buffer] |
472 | sub eax, 8 |
472 | sub eax, 8 |
473 | call Kfree |
473 | call Kfree |
474 | ret 20 |
474 | ret 20 |
475 | endp |
475 | endp |