Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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