Subversion Repositories Kolibri OS

Rev

Rev 5051 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5051 clevermous 1
; standard driver stuff; version of driver model = 5
2
format PE DLL native 0.05
3
entry START
3709 clevermous 4
 
5
DEBUG = 1
6
 
7
; this is for DEBUGF macro from 'fdo.inc'
8
__DEBUG__ = 1
9
__DEBUG_LEVEL__ = 1
10
 
11
include '../../struct.inc'
12
 
13
; Compile-time settings.
14
; If set, the code will dump all descriptors as they are read to the debug board.
15
USB_DUMP_DESCRIPTORS = 1
16
; If set, the code will dump any unclaimed input to the debug board.
17
HID_DUMP_UNCLAIMED = 1
18
 
19
; USB constants
20
DEVICE_DESCR_TYPE           = 1
21
CONFIG_DESCR_TYPE           = 2
22
STRING_DESCR_TYPE           = 3
23
INTERFACE_DESCR_TYPE        = 4
24
ENDPOINT_DESCR_TYPE         = 5
25
DEVICE_QUALIFIER_DESCR_TYPE = 6
26
 
27
CONTROL_PIPE     = 0
28
ISOCHRONOUS_PIPE = 1
29
BULK_PIPE        = 2
30
INTERRUPT_PIPE   = 3
31
 
32
; USB HID constants
33
HID_DESCR_TYPE      = 21h
34
REPORT_DESCR_TYPE   = 22h
35
PHYSICAL_DESCR_TYPE = 23h
36
 
37
; USB structures
38
struct config_descr
39
bLength                 db      ?
40
bDescriptorType         db      ?
41
wTotalLength            dw      ?
42
bNumInterfaces          db      ?
43
bConfigurationValue     db      ?
44
iConfiguration          db      ?
45
bmAttributes            db      ?
46
bMaxPower               db      ?
47
ends
48
 
49
struct interface_descr
50
bLength                 db      ?
51
bDescriptorType         db      ?
52
bInterfaceNumber        db      ?
53
bAlternateSetting       db      ?
54
bNumEndpoints           db      ?
55
bInterfaceClass         db      ?
56
bInterfaceSubClass      db      ?
57
bInterfaceProtocol      db      ?
58
iInterface              db      ?
59
ends
60
 
61
struct endpoint_descr
62
bLength                 db      ?
63
bDescriptorType         db      ?
64
bEndpointAddress        db      ?
65
bmAttributes            db      ?
66
wMaxPacketSize          dw      ?
67
bInterval               db      ?
68
ends
69
 
70
; USB HID structures
71
struct hid_descr
3726 clevermous 72
bLength                 db      ?
73
bDescriptorType         db      ?
74
bcdHID                  dw      ?
75
bCountryCode            db      ?
76
bNumDescriptors         db      ?
77
base_sizeof     rb      0
3709 clevermous 78
; now two fields are repeated .bNumDescriptors times:
3726 clevermous 79
subDescriptorType       db      ?
80
subDescriptorLength     dw      ?
3709 clevermous 81
ends
82
 
83
; Include macro for parsing report descriptors/data.
84
macro workers_globals
85
{}
86
include 'report.inc'
87
 
88
; Driver data for all devices
89
struct usb_device_data
3726 clevermous 90
hid                     hid_data        ; data of HID layer
91
epdescr                 dd      ?       ; endpoint descriptor
92
hiddescr                dd      ?       ; HID descriptor
93
interface_number        dd      ?       ; copy of interface_descr.bInterfaceNumber
94
configpipe              dd      ?       ; config pipe handle
95
intpipe                 dd      ?       ; interrupt pipe handle
96
input_transfer_size     dd      ?       ; input transfer size
97
input_buffer            dd      ?       ; buffer for input transfers
98
control                 rb      8       ; control packet to device
3709 clevermous 99
ends
100
 
5051 clevermous 101
section '.flat' code readable writable executable
102
include '../../macros.inc'
103
include '../../proc32.inc'
104
include '../../peimport.inc'
105
include '../../fdo.inc'
3709 clevermous 106
; The start procedure.
107
proc START
108
virtual at esp
3726 clevermous 109
        dd      ?       ; return address
110
.reason dd      ?
5051 clevermous 111
.cmdline 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.
5051 clevermous 121
        invoke  RegUSBDriver, my_driver, eax, usb_functions
3709 clevermous 122
; 3. Return the returned value of RegUSBDriver.
123
.nothing:
5051 clevermous 124
        ret
3709 clevermous 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
5051 clevermous 141
        invoke  Kmalloc
3709 clevermous 142
        test    eax, eax
3726 clevermous 143
        jnz     @f
144
        mov     esi, nomemory_msg
5051 clevermous 145
        invoke  SysMsgBoardStr
3726 clevermous 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
5051 clevermous 218
        invoke  SysMsgBoardStr
3709 clevermous 219
; 6b. Free memory allocated for device data.
220
.free:
221
        xchg    eax, ebx
5051 clevermous 222
        invoke  Kfree
3709 clevermous 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
5051 clevermous 258
        invoke  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:
5051 clevermous 298
        invoke  SysMsgBoardStr
3726 clevermous 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
5051 clevermous 307
        invoke  Kmalloc
3726 clevermous 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
5051 clevermous 323
        invoke  USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
3726 clevermous 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
5051 clevermous 329
        invoke  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
5051 clevermous 356
        invoke  SysMsgBoardStr
3726 clevermous 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]
5051 clevermous 389
        invoke  USBOpenPipe, [ebx+usb_device_data.configpipe], ecx, edx, INTERRUPT_PIPE, eax
3726 clevermous 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
5051 clevermous 421
        invoke  Kmalloc
3726 clevermous 422
        test    eax, eax
423
        jnz     @f
424
        mov     esi, nomemory_msg
5051 clevermous 425
        invoke  SysMsgBoardStr
3726 clevermous 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]
5051 clevermous 433
        invoke  Kfree
3726 clevermous 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
5051 clevermous 442
        invoke  USBNormalTransferAsync, \
3726 clevermous 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
5051 clevermous 495
        invoke  SysMsgBoardStr
3726 clevermous 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
5051 clevermous 510
        invoke  SysMsgBoardStr
3709 clevermous 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
5051 clevermous 515
        invoke  Kfree
3709 clevermous 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'
5148 hidnplayr 526
include 'multimedia.inc'
3709 clevermous 527
 
528
; strings
529
my_driver       db      'usbhid',0
3726 clevermous 530
nomemory_msg    db      'K : no memory',13,10,0
3709 clevermous 531
invalid_config_descr_msg db 'K : invalid config descriptor',13,10,0
3726 clevermous 532
reportfail      db      'K : failed to read report descriptor',13,10,0
533
transfer_error_msg db   'K : USB transfer error, disabling HID device',13,10,0
534
disconnectmsg   db      'K : USB HID device disconnected',13,10,0
535
invalid_report_msg db   'K : report descriptor is invalid',13,10,0
536
delimiter_note  db      'K : note: alternate usage ignored',13,10,0
3709 clevermous 537
 
538
align 4
539
; Structure with callback functions.
540
usb_functions:
541
        dd      12
542
        dd      AddDevice
543
        dd      DeviceDisconnected
544
 
545
; for DEBUGF macro
546
include_debug_strings
547
 
548
; Workers data
549
workers_globals
550
 
5051 clevermous 551
align 4
552
data fixups
553
end data