Subversion Repositories Kolibri OS

Rev

Rev 4592 | Go to most recent revision | 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'
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
align 4
538
; Structure with callback functions.
539
usb_functions:
540
        dd      12
541
        dd      AddDevice
542
        dd      DeviceDisconnected
543
 
544
; for DEBUGF macro
545
include_debug_strings
546
 
547
; Workers data
548
workers_globals
549
 
5051 clevermous 550
align 4
551
data fixups
552
end data