Subversion Repositories Kolibri OS

Rev

Rev 4598 | Go to most recent revision | Details | Compare with Previous | 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
3711 clevermous 10
        workers_globals
3709 clevermous 11
align 4
12
; Callbacks for HID layer.
13
keyboard_driver:
3711 clevermous 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
3709 clevermous 20
; Callbacks for keyboard layer.
21
kbd_functions:
3711 clevermous 22
        dd      12
23
        dd      CloseKeyboard
24
        dd      SetKeyboardLights
3709 clevermous 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.
3711 clevermous 27
EX = 80h        ; if set, precede the scancode with special scancode 0xE0
3709 clevermous 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
3711 clevermous 34
        db      10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h, 04h, 05h, 06h, 07h
4598 clevermous 35
        db      08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah, 1Bh, 2Bh, 2Bh, 27h
3709 clevermous 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
3711 clevermous 38
        db      35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h, 51h, 4Bh, 4Ch, 4Dh, 47h, 48h, 49h, 52h, 53h
4932 hidnplayr 39
        db      56h,5Dh+EX,5Eh+EX
3709 clevermous 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
3711 clevermous 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
3709 clevermous 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.
3711 clevermous 62
        movi    eax, sizeof.keyboard_device_data
63
        call    Kmalloc
64
        test    eax, eax
65
        jz      .nothing
3709 clevermous 66
; 2. Initialize keyboard_device_data: store pointer to USB layer data,
67
; zero some fields, initialize bit positions to -1.
3711 clevermous 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
3709 clevermous 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.
3711 clevermous 81
        push    ebx esi
3709 clevermous 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.
3711 clevermous 85
        mov     edx, [edi+collection.output.first_report]
86
        test    edx, edx
87
        jz      .led_report_set
3709 clevermous 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.
3711 clevermous 92
        mov     ecx, [edx+report.first_field]
3709 clevermous 93
.scan_led_field:
94
; Process one field group.
95
; 3d. If there are no more field groups, exit the loop over field groups.
3711 clevermous 96
        test    ecx, ecx
97
        jz      .next_led_report
3709 clevermous 98
; For now, assume that all LEDs are plain variable fields, not arrays.
99
; 3e. Ignore array field groups.
3711 clevermous 100
        test    byte [ecx+report_field_group.flags], HID_FIELD_VARIABLE
101
        jz      .next_led_field
3709 clevermous 102
; 3f. Loop over all fields in the current group.
3711 clevermous 103
        push    [ecx+report_field_group.count]
3709 clevermous 104
; esi = pointer to usage of the current field
3711 clevermous 105
        lea     esi, [ecx+report_field_group.common_sizeof]
3709 clevermous 106
; ebx = bit position of the current field
3711 clevermous 107
        mov     ebx, [ecx+report_field_group.offset]
3709 clevermous 108
; if report is numbered, add extra byte in the start of report
3711 clevermous 109
        cmp     [edx+report.id], 0
110
        jz      .scan_led_usage
111
        add     ebx, 8
3709 clevermous 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
3711 clevermous 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
3709 clevermous 121
.scrolllock:
3711 clevermous 122
        mov     [eax+keyboard_device_data.scrolllock_bit], ebx
123
        jmp     @f
3709 clevermous 124
.capslock:
3711 clevermous 125
        mov     [eax+keyboard_device_data.capslock_bit], ebx
126
        jmp     @f
3709 clevermous 127
.numlock:
3711 clevermous 128
        mov     [eax+keyboard_device_data.numlock_bit], ebx
3709 clevermous 129
@@:
3711 clevermous 130
        mov     [eax+keyboard_device_data.led_report], edx
3709 clevermous 131
.next_field:
3711 clevermous 132
        add     esi, 4
133
        add     ebx, [ecx+report_field_group.size]
134
        dec     dword [esp]
135
        jnz     .scan_led_usage
136
        pop     ebx
3709 clevermous 137
.next_led_field:
138
; 3g. Continue loop over field groups: get next field group.
3711 clevermous 139
        mov     ecx, [ecx+report_field_group.next]
140
        jmp     .scan_led_field
3709 clevermous 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.
3711 clevermous 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
3709 clevermous 150
.led_report_set:
151
; 3i. Restore registers.
3711 clevermous 152
        pop     esi ebx
3709 clevermous 153
; 4. Register keyboard in the kernel.
154
; store pointer to keyboard_device_data in the stack
3711 clevermous 155
        push    eax
3709 clevermous 156
; call kernel API
3711 clevermous 157
        stdcall RegKeyboard, kbd_functions, eax
3709 clevermous 158
; restore pointer to keyboard_device_data from the stack,
159
; putting keyboard handle from API to the stack
3711 clevermous 160
        xchg    eax, [esp]
3709 clevermous 161
; put keyboard handle from API from the stack to keyboard_device_data field
3711 clevermous 162
        pop     [eax+keyboard_device_data.handle]
3709 clevermous 163
; If failed, free keyboard_device_data and return NULL.
3711 clevermous 164
        cmp     [eax+keyboard_device_data.handle], 0
165
        jz      .fail_free
3709 clevermous 166
; 5. Return pointer to keyboard_device_data.
167
.nothing:
3711 clevermous 168
        ret
3709 clevermous 169
.fail_free:
3711 clevermous 170
        call    Kfree
171
        xor     eax, eax
172
        ret
3709 clevermous 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.
3711 clevermous 180
        cmp     [edi+keyboard_device_data.timer], 0
181
        jz      @f
3709 clevermous 182
        stdcall CancelTimerHS, [edi+keyboard_device_data.timer]
183
@@:
184
; 2. Unregister keyboard in the kernel.
3711 clevermous 185
        stdcall DelKeyboard, [edi+keyboard_device_data.handle]
3709 clevermous 186
; We should free data in CloseKeyboard, not here.
3711 clevermous 187
        ret
3709 clevermous 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.
3711 clevermous 195
        ret
3709 clevermous 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.
3711 clevermous 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
3709 clevermous 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.
3711 clevermous 223
        lea     eax, [ecx-USAGE_KBD_FIRST_KEY]
224
        cmp     eax, normal_keys_number
225
        jae     .not_normal_key
3709 clevermous 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.
3711 clevermous 229
        cmp     [normal_keys + eax], 0
230
        jz      .unclaimed
3709 clevermous 231
; 1c. Get the scancode.
3711 clevermous 232
        movzx   ecx, [normal_keys + eax]
3709 clevermous 233
; 1d. Further actions are slightly different for key press and key release.
234
; Decide what to do.
3711 clevermous 235
        test    edx, edx
236
        jz      .normal_key_released
3709 clevermous 237
.normal_key_pressed:
238
; The key is pressed.
239
; 1e. Store the last pressed key for autorepeat.
3711 clevermous 240
        mov     [edi+keyboard_device_data.repeatkey], cl
3709 clevermous 241
; 1f. Copy bit 7 to CF and send scancode with bit 7 cleared.
3711 clevermous 242
        btr     ecx, 7
243
        call    .send_key
3709 clevermous 244
; 1g. Stop the previous autorepeat timer, if any.
3711 clevermous 245
        mov     eax, [edi+keyboard_device_data.timer]
246
        test    eax, eax
247
        jz      @f
248
        stdcall CancelTimerHS, eax
3709 clevermous 249
@@:
250
; 1h. Start the new autorepeat timer with 250 ms initial delay
251
; and 50 ms subsequent delays.
3711 clevermous 252
        stdcall TimerHS, 25, 5, autorepeat_timer, edi
253
        mov     [edi+keyboard_device_data.timer], eax
3709 clevermous 254
if ~HID_DUMP_UNCLAIMED
255
.unclaimed:
256
end if
3711 clevermous 257
        ret
3709 clevermous 258
.normal_key_released:
259
; The key is released.
260
; 1i. Stop the autorepeat timer if it is autorepeating the released key.
3711 clevermous 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
3709 clevermous 270
@@:
3711 clevermous 271
        pop     ecx
3709 clevermous 272
.no_stop_timer:
273
; 1j. Copy bit 7 to CF and send scancode with bit 7 set.
3711 clevermous 274
        bts     ecx, 7
275
        call    .send_key
276
        ret
3709 clevermous 277
.not_normal_key:
278
; 2. USAGE_KBD_NOEVENT is simply a filler for free array fields,
279
; ignore it.
3711 clevermous 280
        cmp     ecx, USAGE_KBD_NOEVENT
281
        jz      .nothing
3709 clevermous 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.
3711 clevermous 285
        lea     eax, [ecx-USAGE_KBD_LCTRL]
286
        cmp     eax, 8
287
        jae     .unclaimed
3709 clevermous 288
; 3b. Further actions are slightly different for modifier press
289
; and modifier release. Decide what to do.
3711 clevermous 290
        test    edx, edx
291
        jz      .modifier_not_pressed
3709 clevermous 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.
3711 clevermous 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
3709 clevermous 302
@@:
303
.nothing:
3711 clevermous 304
        ret
3709 clevermous 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.
3711 clevermous 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
3709 clevermous 315
@@:
3711 clevermous 316
        ret
3709 clevermous 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:
3711 clevermous 321
        jnc     @f
322
        push    ecx
323
        mov     ecx, 0xE0
324
        call    SetKeyboardData
325
        pop     ecx
3709 clevermous 326
@@:
3711 clevermous 327
        call    SetKeyboardData
328
        ret
3709 clevermous 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.
3711 clevermous 336
        ret
3709 clevermous 337
endp
338
 
339
; Timer callback for SetTimerHS.
340
proc autorepeat_timer
341
virtual at esp
3711 clevermous 342
                dd      ?       ; return address
343
.data           dd      ?
3709 clevermous 344
end virtual
345
; Just resend the last pressed key.
3711 clevermous 346
        mov     eax, [.data]
347
        movzx   ecx, [eax+keyboard_device_data.repeatkey]
3709 clevermous 348
; Copy bit 7 to CF and send scancode with bit 7 cleared.
3711 clevermous 349
        btr     ecx, 7
350
        call    keyboard_driver_input_field.send_key
351
        ret     4
3709 clevermous 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
3711 clevermous 358
                dd      ?       ; return address
359
.device_data    dd      ?
3709 clevermous 360
end virtual
3711 clevermous 361
        mov     eax, [.device_data]
362
        call    Kfree
363
        ret     4
3709 clevermous 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
3711 clevermous 370
size    dd      ?
3709 clevermous 371
endl
372
; 1. Get the pointer to the LED report.
373
; If there is no LED report, exit from the function.
3711 clevermous 374
        mov     ebx, [device_data]
375
        mov     esi, [ebx+keyboard_device_data.led_report]
376
        test    esi, esi
377
        jz      .nothing
3709 clevermous 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.
3711 clevermous 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
3709 clevermous 387
@@:
3711 clevermous 388
        mov     [size], eax
3709 clevermous 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.
3711 clevermous 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
3709 clevermous 399
; 4. Zero-initialize output report.
3711 clevermous 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
3709 clevermous 407
; 5. Store report ID, if assigned. If not assigned, that would just write zero
408
; over zeroes.
3711 clevermous 409
        mov     edx, [esi+report.id]
410
        mov     [edi], edx
3709 clevermous 411
; 6. Set report bits corresponding to active indicators.
3711 clevermous 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
3709 clevermous 419
@@:
3711 clevermous 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
3709 clevermous 426
@@:
3711 clevermous 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
3709 clevermous 433
@@:
434
; 7. Fill setup packet.
3711 clevermous 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
3709 clevermous 446
; 8. Submit output control request.
3711 clevermous 447
        stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
448
                eax, edi, [size], after_set_keyboard_lights, ebx, 0
3709 clevermous 449
; If failed, free the buffer now.
450
; If succeeded, the callback will free the buffer.
3711 clevermous 451
        test    eax, eax
452
        jnz     .nothing
453
        lea     eax, [edi-8]
454
        call    Kfree
3709 clevermous 455
.nothing:
3711 clevermous 456
        ret
3709 clevermous 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
3711 clevermous 463
                dd      ?       ; return address
464
.pipe           dd      ?
465
.status         dd      ?
466
.buffer         dd      ?
467
.length         dd      ?
468
.calldata       dd      ?
3709 clevermous 469
end virtual
470
; Ignore status, just free the buffer allocated by SetKeyboardLights.
3711 clevermous 471
        mov     eax, [.buffer]
472
        sub     eax, 8
473
        call    Kfree
474
        ret     20
3709 clevermous 475
endp