Subversion Repositories Kolibri OS

Rev

Rev 4529 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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