Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3709 clevermous 1
; Parser of HID structures: parse HID report descriptor,
2
; parse/generate input/output/feature reports.
3
 
4
; =============================================================================
5
; ================================= Constants =================================
6
; =============================================================================
7
; Usage codes from HID specification
8
; Generic Desktop usage page
9
USAGE_GD_POINTER     = 10001h
10
USAGE_GD_MOUSE       = 10002h
11
USAGE_GD_JOYSTICK    = 10004h
12
USAGE_GD_GAMEPAD     = 10005h
13
USAGE_GD_KEYBOARD    = 10006h
14
USAGE_GD_KEYPAD      = 10007h
15
 
16
USAGE_GD_X           = 10030h
17
USAGE_GD_Y           = 10031h
18
USAGE_GD_Z           = 10032h
19
USAGE_GD_RX          = 10033h
20
USAGE_GD_RY          = 10034h
21
USAGE_GD_RZ          = 10035h
22
USAGE_GD_SLIDER      = 10036h
23
USAGE_GD_DIAL        = 10037h
24
USAGE_GD_WHEEL       = 10038h
25
 
26
; Keyboard/Keypad usage page
27
USAGE_KBD_NOEVENT    = 70000h
28
USAGE_KBD_ROLLOVER   = 70001h
29
USAGE_KBD_POSTFAIL   = 70002h
30
USAGE_KBD_FIRST_KEY  = 70004h ; this is 'A', actually
31
USAGE_KBD_LCTRL      = 700E0h
32
USAGE_KBD_LSHIFT     = 700E1h
33
USAGE_KBD_LALT       = 700E2h
34
USAGE_KBD_LWIN       = 700E3h
35
USAGE_KBD_RCTRL      = 700E4h
36
USAGE_KBD_RSHIFT     = 700E5h
37
USAGE_KBD_RALT       = 700E6h
38
USAGE_KBD_RWIN       = 700E7h
39
 
40
; LED usage page
41
USAGE_LED_NUMLOCK    = 80001h
42
USAGE_LED_CAPSLOCK   = 80002h
43
USAGE_LED_SCROLLLOCK = 80003h
44
 
45
; Button usage page
46
; First button is USAGE_BUTTON_PAGE+1, second - USAGE_BUTTON_PAGE+2 etc.
47
USAGE_BUTTON_PAGE    = 90000h
48
 
49
; Flags for input/output/feature fields
50
HID_FIELD_CONSTANT   = 1 ; if not, then Data field
51
HID_FIELD_VARIABLE   = 2 ; if not, then Array field
52
HID_FIELD_RELATIVE   = 4 ; if not, then Absolute field
53
HID_FIELD_WRAP       = 8
54
HID_FIELD_NONLINEAR  = 10h
55
HID_FIELD_NOPREFERRED= 20h ; no preferred state
56
HID_FIELD_HASNULL    = 40h ; has null state
57
HID_FIELD_VOLATILE   = 80h ; for output/feature fields
58
HID_FIELD_BUFBYTES   = 100h; buffered bytes
59
 
60
; Report descriptor can easily describe gigabytes of (meaningless) data.
61
; Keep report size reasonable to avoid excessive memory allocations and
62
; calculation overflows; 1 Kb is more than enough (typical size is 3-10 bytes).
63
MAX_REPORT_BYTES = 1024
64
 
65
; =============================================================================
66
; ================================ Structures =================================
67
; =============================================================================
68
; Every meaningful report field group has one or more associated usages.
69
; Usages can be individual or joined into continuous ranges.
70
; This structure describes one range or one individual usage in a large array;
71
; individual usage is equivalent to a range of length 1.
72
struct usage_range
73
offset		dd	?
74
; Sum of range sizes over all previous array items.
75
; Size of range a equals
76
; [a + sizeof.usage_range + usage_range.offset] - [a + usage_range.offset].
77
; The total sum over all array items immediately follows the array,
78
; this field must be the first so that the formula above works for the last item.
79
first_usage	dd	?
80
; Usage code for first item in the range.
81
ends
82
 
83
; This structure describes one group of report fields with identical properties.
84
struct report_field_group
85
next		dd	?
86
; All field groups in one report are organized in a single-linked list.
87
; This is the next group in the report or 0 for the last group.
88
size		dd	?
89
; Size in bits of one field. Cannot be zero or greater than 32.
90
count		dd	?	; field count, cannot be zero
91
offset		dd	?	; offset from report start, in bits
92
; Following fields are decoded from report descriptor, see HID spec for details.
93
flags		dd	?
94
logical_minimum dd	?
95
logical_maximum dd	?
96
physical_minimum dd	?
97
physical_maximum dd	?
98
unit_exponent	dd	?
99
unit		dd	?
100
; Following fields are used to speedup extract_field_value.
101
mask		dd	?
102
; Bitmask for all data bits except sign bit:
103
; (1 shl .size) - 1 for unsigned fields, (1 shl (.size-1)) - 1 for signed fields
104
sign_mask	dd	?
105
; Zero for unsigned fields. Bitmask with sign bit set for signed fields.
106
common_sizeof	rd	0
107
; Variable and Array field groups differ significantly.
108
; Variable field groups are simple. There are .count fields, each field has
109
; predefined Usage, the content of a field is its value. Each field is
110
; always present in the report. For Variable field groups, we just keep
111
; additional .count dwords with usages for individual fields.
112
; Array field groups are complicated. There are .count uniform fields.
113
; The content of a field determines Usage; Usages which are currently presented
114
; in the report have value = 1, other Usages have value = 0. The number of
115
; possible Usages is limited only by field .size; 32-bit field could encode any
116
; Usage, so it is unreasonable to keep all Usages in the plain array, as with
117
; Variable fields. However, many unrelated Usages in one group are meaningless,
118
; so usually possible values are grouped in sequential ranges; number of ranges
119
; is limited by report descriptor size (max 0xFFFF bytes should contain all
120
; information, including usage ranges and field descriptions).
121
; Also, for Array variables we pass changes in state to drivers, not the state
122
; itself, because sending information about all possible Usages is inpractical;
123
; so we should remember the previous state in addition to the current state.
124
; Thus, for Array variables keep the following information, in this order:
125
; * some members listed below; note that they do NOT exist for Variable groups;
126
; * array of usage ranges in form of usage_range structures, including
127
;   an additional dword after array described in usage_range structure;
128
; * allocated memory for current values of the report;
129
; * values of the previous report.
130
num_values_prev dd	?	; number of values in the previous report
131
num_usage_ranges dd	?	; number of usage_range, always nonzero
132
usages		rd	0
133
ends
134
 
135
; This structure describes one report.
136
; All reports of one type are organized into a single-linked list.
137
struct report
138
next		dd	?	; pointer to next report of the same type, if any
139
size		dd	?	; total size in bits
140
first_field	dd	?	; pointer to first report_field_group for this report
141
last_field	dd	?
142
; pointer to last report_field_group for this report, if any;
143
; address of .first_field, if .first_field is 0
144
id		dd	?
145
; Report ID, if assigned. Zero otherwise.
146
top_level_collection dd ?	; top-level collection for this report
147
ends
148
 
149
; This structure describes a set of reports of the same type;
150
; there are 3 sets (possibly empty), input, output and feature.
151
struct report_set
152
data		dd	?
153
; If .numbered is zero, this is zero for the empty set and
154
; a pointer to the (only) report structure otherwise.
155
; If .numbered is nonzero, this is a pointer to 256-dword array of pointers
156
; to reports organized by report ID.
157
first_report	dd	?
158
; Pointer to the first report or 0 for the empty set.
159
numbered	db	?
160
; If zero, report IDs are not used, there can be at most one report in the set.
161
; If nonzero, first byte of the report is report ID.
162
		rb	3	; padding
163
ends
164
 
165
; This structure describes a range of reports of one type that belong to
166
; some collection.
167
struct collection_report_set
168
first_report	dd	?
169
first_field	dd	?
170
last_report	dd	?
171
last_field	dd	?
172
ends
173
 
174
; This structure defines driver callbacks which are used while
175
; device is active; i.e. all callbacks except add_device.
176
struct hid_driver_active_callbacks
177
disconnect	dd	?
178
; Called when an existing HID device is disconnected.
179
;
180
; Four following functions are called when a new input packet arrives
181
; in the following order: .begin_packet, then .input_field several times
182
; for each input field, interleaved with .array_overflow? for array groups,
183
; then .end_packet.
184
begin_packet	dd	?
185
; edi -> driver data
186
array_overflow?	dd	?
187
; edi -> driver data
188
; out: CF cleared <=> ignore this array
189
input_field	dd	?
190
; edi -> driver data, ecx = usage, edx = value
191
end_packet	dd	?
192
; edi -> driver data
193
ends
194
 
195
; This structure describes one collection.
196
struct collection
197
next		dd	?	; pointer to the next collection in the same level
198
				; must be the first field
199
parent		dd	?	; pointer to nesting collection
200
first_child	dd	?	; pointer to the first nested collection
201
last_child	dd	?	; pointer to the last nested collection
202
				; or to .first_child, if .first_child is zero
203
type		dd	?	; Application, Physical etc
204
usage		dd	?	; associated Usage code
205
; Next fields are filled only for top-level collections.
206
callbacks	hid_driver_active_callbacks
207
driver_data	dd	?	; value to be passed as is to driver callbacks
208
input		collection_report_set
209
output		collection_report_set
210
feature		collection_report_set
211
ends
212
 
213
; This structure keeps all data used by the HID layer for one device.
214
struct hid_data
215
input		report_set
216
output		report_set
217
feature		report_set
218
first_collection	dd	?
219
ends
220
 
221
; This structure defines callbacks required from the driver.
222
struct hid_driver_callbacks
223
add_device	dd	?
224
; Called when a new HID device is connected.
225
active		hid_driver_active_callbacks
226
ends
227
 
228
; Two following structures describe temporary data;
229
; the corresponding objects cease to exist when HID parser completes
230
; state of Global items
231
struct global_items
232
next			dd	?
233
usage_page		dd	?
234
logical_minimum		dd	?
235
logical_maximum		dd	?
236
physical_minimum	dd	?
237
physical_maximum	dd	?
238
unit_exponent		dd	?
239
unit			dd	?
240
report_size		dd	?
241
report_id		dd	?
242
report_count		dd	?
243
ends
244
 
245
; one range of Usages
246
struct usage_list_item
247
next			dd	?
248
first_usage		dd	?
249
num_usages		dd	?
250
ends
251
 
252
; =============================================================================
253
; =================================== Code ====================================
254
; =============================================================================
255
 
256
macro workers_globals
257
{
258
	workers_globals
259
; Jump tables for switch'ing in the code.
260
align 4
261
; jump table for two lower bits which encode size of item data
262
parse_descr_label.fetch_jumps:
263
	dd	parse_descr_label.fetch_none	; x0, x4, x8, xC
264
	dd	parse_descr_label.fetch_byte	; x1, x5, x9, xD
265
	dd	parse_descr_label.fetch_word	; x2, x6, xA, xE
266
	dd	parse_descr_label.fetch_dword	; x3, x7, xB, xF
267
; jump table for two next bits which encode item type
268
parse_descr_label.type_jumps:
269
	dd	parse_descr_label.parse_main
270
	dd	parse_descr_label.parse_global
271
	dd	parse_descr_label.parse_local
272
	dd	parse_descr_label.parse_reserved
273
; jump table for 4 upper bits in the case of Main item
274
parse_descr_label.main_jumps:
275
	dd	parse_descr_label.input	; 80...83
276
	dd	parse_descr_label.output	; 90...93
277
	dd	parse_descr_label.collection	; A0...A3
278
	dd	parse_descr_label.feature	; B0...B3
279
	dd	parse_descr_label.end_collection ; C0...C3
280
parse_descr_label.num_main_items = ($ - parse_descr_label.main_jumps) / 4
281
; jump table for 4 upper bits in the case of Global item
282
parse_descr_label.global_jumps:
283
	dd	parse_descr_label.usage_page	; 04...07
284
	dd	parse_descr_label.logical_minimum ; 14...17
285
	dd	parse_descr_label.logical_maximum ; 24...27
286
	dd	parse_descr_label.physical_minimum ; 34...37
287
	dd	parse_descr_label.physical_maximum ; 44...47
288
	dd	parse_descr_label.unit_exponent ; 54...57
289
	dd	parse_descr_label.unit		; 64...67
290
	dd	parse_descr_label.report_size	; 74...77
291
	dd	parse_descr_label.report_id	; 84...87
292
	dd	parse_descr_label.report_count	; 94...97
293
	dd	parse_descr_label.push		; A4...A7
294
	dd	parse_descr_label.pop		; B4...B7
295
parse_descr_label.num_global_items = ($ - parse_descr_label.global_jumps) / 4
296
; jump table for 4 upper bits in the case of Local item
297
parse_descr_label.local_jumps:
298
	dd	parse_descr_label.usage	; 08...0B
299
	dd	parse_descr_label.usage_minimum ; 18...1B
300
	dd	parse_descr_label.usage_maximum ; 28...2B
301
	dd	parse_descr_label.item_parsed	; 38...3B = designator item; ignore
302
	dd	parse_descr_label.item_parsed	; 48...4B = designator minimum; ignore
303
	dd	parse_descr_label.item_parsed	; 58...5B = designator maximum; ignore
304
	dd	parse_descr_label.item_parsed	; 68...6B not assigned
305
	dd	parse_descr_label.item_parsed	; 78...7B = string index; ignore
306
	dd	parse_descr_label.item_parsed	; 88...8B = string minimum; ignore
307
	dd	parse_descr_label.item_parsed	; 98...9B = string maximum; ignore
308
	dd	parse_descr_label.delimiter	; A8...AB
309
parse_descr_label.num_local_items = ($ - parse_descr_label.local_jumps) / 4
310
}
311
 
312
; Local variables for parse_descr.
313
macro parse_descr_locals
314
{
315
cur_item_size	dd	?	; encoded size of data for current item
316
report_ok	db	?	; 0 on error, 1 if everything is ok
317
field_type	db	?	; 0/1/2 for input/output/feature fields
318
		rb	2	; alignment
319
field_data	dd	?	; data for current item when it describes a field group
320
last_reports	rd	3	; pointers to last input/output/feature records
321
usage_minimum	dd	?	; current value of Usage Minimum
322
usage_list	dd	?	; list head of usage_list_item
323
usage_tail	dd	?	; list tail of usage_list_item
324
num_usage_ranges dd	?	; number of usage ranges, size of usage_list
325
delimiter_depth	dd	?	; normally 0; 1 inside of Delimiter();
326
				; nested Delimiter()s are not allowed
327
usage_variant	dd	?	; 0 outside of Delimiter()s and for first Usage inside Delimiter(),
328
				; incremented with each new Usage inside Delimiter()
329
cur_collection	dd	?	; current collection
330
last_collection	dd	?	; last top-level collection
331
}
332
 
333
; Parse report descriptor. The caller should provide local variables
334
; [buffer] = pointer to report descriptor, [length] = length of report descriptor,
335
; [calldata] = pointer to hid_data (possibly wrapped in a large structure).
336
macro parse_descr
337
{
338
parse_descr_label:
339
; 1. Initialize.
340
; 1a. Set some variables to initial values.
341
	xor	edi, edi
342
	mov	dword [report_ok], edi
343
	mov	[usage_list], edi
344
	mov	[cur_collection], edi
345
	mov	eax, [calldata]
346
	add	eax, hid_data.input.first_report
347
	mov	[last_reports+0*4], eax
348
	add	eax, hid_data.output.first_report - hid_data.input.first_report
349
	mov	[last_reports+1*4], eax
350
	add	eax, hid_data.feature.first_report - hid_data.output.first_report
351
	mov	[last_reports+2*4], eax
352
	add	eax, hid_data.first_collection - hid_data.feature.first_report
353
	mov	[last_collection], eax
354
; 1b. Allocate state of global items.
355
	movi	eax, sizeof.global_items
356
	call	Kmalloc
357
	test	eax, eax
358
	jz	.memory_error
359
; 1c. Zero-initialize it and move pointer to edi.
360
	push	eax
361
	xchg	eax, edi
362
	movi	ecx, sizeof.global_items / 4
363
	rep	stosd
364
	pop	edi
365
; 1d. Load pointer to data into esi and make [length] point to end of data.
366
	mov	esi, [buffer]
367
	add	[length], esi
368
; 2. Clear all local items.
369
; This is needed in the beginning and after processing any Main item.
370
.zero_local_items:
371
	mov	eax, [usage_list]
372
@@:
373
	test	eax, eax
374
	jz	@f
375
	push	[eax+usage_list_item.next]
376
	call	Kfree
377
	pop	eax
378
	jmp	@b
379
@@:
380
	lea	ecx, [usage_list]
381
	mov	[usage_tail], ecx
382
	mov	[ecx], eax
383
	mov	[delimiter_depth], eax
384
	mov	[usage_variant], eax
385
	mov	[usage_minimum], eax
386
	mov	[num_usage_ranges], eax
387
; 3. Parse items until end of data found.
388
	cmp	esi, [length]
389
	jae	.parse_end
390
.fetch_next_item:
391
; --------------------------------- Parse item --------------------------------
392
; 4. Parse one item.
393
; 4a. Get item data. eax = first item byte = code+type+size (4+2+2 bits),
394
; ebx = item data interpreted as unsigned,
395
; ecx = item data interpreted as signed.
396
	movzx	eax, byte [esi]
397
	mov	ecx, eax
398
	and	ecx, 3
399
	mov	[cur_item_size], ecx
400
	jmp	dword [.fetch_jumps+ecx*4]
401
.invalid_report:
402
	mov	esi, invalid_report_msg
403
	jmp	.end_str
404
.fetch_none:
405
	xor	ebx, ebx
406
	xor	ecx, ecx
407
	inc	esi
408
	jmp	.fetched
409
.fetch_byte:
410
	add	esi, 2
411
	cmp	esi, [length]
412
	ja	.invalid_report
413
	movzx	ebx, byte [esi-1]
414
	movsx	ecx, bl
415
	jmp	.fetched
416
.fetch_word:
417
	add	esi, 3
418
	cmp	esi, [length]
419
	ja	.invalid_report
420
	movzx	ebx, word [esi-2]
421
	movsx	ecx, bx
422
	jmp	.fetched
423
.fetch_dword:
424
	add	esi, 5
425
	cmp	esi, [length]
426
	ja	.invalid_report
427
	mov	ebx, dword [esi-4]
428
	mov	ecx, ebx
429
.fetched:
430
; 4b. Select the branch according to item type.
431
; For every type, select the concrete handler and go there.
432
	mov	edx, eax
433
	shr	edx, 2
434
	and	edx, 3
435
	shr	eax, 4
436
	jmp	dword [.type_jumps+edx*4]
437
; -------------------------------- Main items ---------------------------------
438
.parse_main:
439
	sub	eax, 8
440
	cmp	eax, .num_main_items
441
	jae	.item_parsed
442
	jmp	dword [.main_jumps+eax*4]
443
; There are 5 Main items.
444
; Input/Output/Feature items create new field groups in the corresponding report;
445
; Collection item opens a new collection (possibly nested),
446
; End Collection item closes the most nested collection.
447
.output:
448
	mov	[field_type], 1
449
	jmp	.new_field
450
.feature:
451
	mov	[field_type], 2
452
	jmp	.new_field
453
.input:
454
	mov	[field_type], 0
455
.new_field:
456
; Create a new field group.
457
	mov	[field_data], ebx
458
	movzx	ebx, [field_type]
459
if sizeof.report_set = 12
460
	lea	ebx, [ebx*3]
461
	shl	ebx, 2
462
else
463
err Change the code
464
end if
465
	add	ebx, [calldata]
466
; 5. Sanity checks: field size and fields count must be nonzero,
467
; field size cannot be more than 32 bits,
468
; if field count is more than MAX_REPORT_SIZE * 8, the report would be more than
469
; MAX_REPORT_SIZE bytes, so it is invalid too.
470
; More precise check for size occurs later; this check only guarantees that
471
; there will be no overflows during subsequent calculations.
472
	cmp	[edi+global_items.report_size], 0
473
	jz	.invalid_report
474
	cmp	[edi+global_items.report_size], 32
475
	ja	.invalid_report
476
; There are devices with Report Count(0) + Input(Constant Variable),
477
; zero-length padding. Thus, do not consider descriptors with Report Count(0)
478
; as invalid; instead, just ignore fields with Report Count(0).
479
	cmp	[edi+global_items.report_count], 0
480
	jz	.zero_local_items
481
	cmp	[edi+global_items.report_count], MAX_REPORT_BYTES * 8
482
	ja	.invalid_report
483
; 6. Get the pointer to the place for the corresponding report in ebx.
484
; 6a. If report ID is not assigned, ebx already points to report_set.data,
485
; so go to 7.
486
	cmp	[edi+global_items.report_id], 0
487
	jz	.report_ptr_found
488
; 6b. If table for reports was already allocated,
489
; go to 6d skipping the next substep.
490
	cmp	[ebx+report_set.numbered], 0
491
	jnz	.report_set_allocated
492
; 6c. This is the first report with ID;
493
; allocate and zero-initialize table for reports.
494
; Note: it is incorrect but theoretically possible that some fields were
495
; already allocated in report without ID; if so, abort processing with error.
496
	cmp	[ebx+report_set.data], 0
497
	jnz	.invalid_report
498
	mov	eax, 256*4
499
	call	Kmalloc
500
	test	eax, eax
501
	jz	.memory_error
502
	mov	[ebx+report_set.data], eax
503
	inc	[ebx+report_set.numbered]
504
	push	edi
505
	mov	edi, eax
506
	mov	ecx, 256
507
	xor	eax, eax
508
	rep	stosd
509
	pop	edi
510
; 6d. Report ID is assigned, report table is allocated,
511
; get the pointer to the corresponding item in the report table.
512
.report_set_allocated:
513
	mov	ebx, [ebx+report_set.data]
514
	mov	ecx, [edi+global_items.report_id]
515
	lea	ebx, [ebx+ecx*4]
516
; 7. If the field group is the first one in the report,
517
; allocate and initialize report without fields.
518
.report_ptr_found:
519
; 7a. Check whether the report has been allocated.
520
	cmp	dword [ebx], 0
521
	jnz	.report_allocated
522
; 7b. Allocate.
523
	movi	eax, sizeof.report
524
	call	Kmalloc
525
	test	eax, eax
526
	jz	.memory_error
527
; 7c. Initialize.
528
	xor	edx, edx
529
	lea	ecx, [eax+report.first_field]
530
	mov	[ebx], eax
531
	mov	[eax+report.next], edx
532
	mov	[eax+report.size], edx
533
	mov	[ecx], edx
534
	mov	[eax+report.last_field], ecx
535
	mov	[eax+report.top_level_collection], edx
536
	mov	ecx, [edi+global_items.report_id]
537
	mov	[eax+report.id], ecx
538
; 7d. Append to the overall list of reports.
539
	movzx	edx, [field_type]
540
	lea	edx, [last_reports+edx*4]
541
	mov	ecx, [edx]
542
	mov	[edx], eax
543
	mov	[ecx], eax
544
.report_allocated:
545
	mov	ebx, [ebx]
546
; ebx points to an already existing report; add new field.
547
; 8. Calculate total size of the group and
548
; check that the new group would not overflow the report.
549
	mov	eax, [edi+global_items.report_size]
550
	mul	[edi+global_items.report_count]
551
	mov	ecx, [ebx+report.size]
552
	add	ecx, eax
553
	cmp	ecx, MAX_REPORT_BYTES * 8
554
	ja	.invalid_report
555
; 9. If there are no usages for this group, this is padding;
556
; add it's size to total report size and stop processing.
557
	cmp	[num_usage_ranges], 0
558
	jz	.padding
559
; 10. Allocate memory for the group: this includes field group structure
560
; and additional fields depending on field type.
561
; See comments in report_field_group structure.
562
	push	eax
563
	mov	edx, [edi+global_items.report_count]
564
	lea	eax, [report_field_group.common_sizeof+edx*4]
565
	test	byte [field_data], HID_FIELD_VARIABLE
566
	jnz	@f
567
	lea	eax, [eax+edx*4]
568
	mov	edx, [num_usage_ranges]
569
	lea	eax, [eax+edx*sizeof.usage_range+4]
570
@@:
571
	call	Kmalloc
572
	pop	edx
573
	test	eax, eax
574
	jz	.memory_error
575
; 11. Update report data.
576
; Field offset is the current report size;
577
; get the current report size and update report size.
578
; Also store the pointer to new field in the previous last field
579
; and update the last field.
580
	mov	ecx, [ebx+report.last_field]
581
	xadd	[ebx+report.size], edx
582
	mov	[ebx+report.last_field], eax
583
	mov	[ecx], eax
584
; 12. Initialize field data: offset was calculated in the previous step,
585
; copy other characteristics from global_items data,
586
; calculate .mask and .sign_mask.
587
	mov	[eax+report_field_group.offset], edx
588
	xor	edx, edx
589
	mov	[eax+report_field_group.next], edx
590
	mov	[eax+report_field_group.sign_mask], edx
591
	inc	edx
592
	mov	ecx, [edi+global_items.report_size]
593
	mov	[eax+report_field_group.size], ecx
594
	shl	edx, cl
595
	cmp	[edi+global_items.logical_minimum], 0
596
	jge	.unsigned
597
	shr	edx, 1
598
	mov	[eax+report_field_group.sign_mask], edx
599
.unsigned:
600
	dec	edx
601
	mov	[eax+report_field_group.mask], edx
602
	mov	ecx, [edi+global_items.report_count]
603
	mov	[eax+report_field_group.count], ecx
604
	mov	ecx, [field_data]
605
	mov	[eax+report_field_group.flags], ecx
606
irps field, logical_minimum logical_maximum physical_minimum physical_maximum unit_exponent unit
607
\{
608
	mov	ecx, [edi+global_items.\#field]
609
	mov	[eax+report_field_group.\#field], ecx
610
\}
611
; 13. Update the current collection; nesting collections will be updated by
612
; end-of-collection handler.
613
	movzx	edx, [field_type]
614
if sizeof.collection_report_set = 16
615
	shl	edx, 4
616
else
617
err Change the code
618
end if
619
	mov	ecx, [cur_collection]
620
	test	ecx, ecx
621
	jz	.no_collection
622
	lea	ecx, [ecx+collection.input+edx]
623
	mov	[ecx+collection_report_set.last_report], ebx
624
	mov	[ecx+collection_report_set.last_field], eax
625
	cmp	[ecx+collection_report_set.first_field], 0
626
	jnz	.no_collection
627
	mov	[ecx+collection_report_set.first_report], ebx
628
	mov	[ecx+collection_report_set.first_field], eax
629
.no_collection:
630
; 14. Transform usage ranges. The target format depends on field type.
631
	test	byte [eax+report_field_group.flags], HID_FIELD_VARIABLE
632
	jz	.transform_usages_for_array
633
; For Variable field groups, expand all ranges to array with .count Usages.
634
; If total number of Usages in all ranges is too large, ignore excessive.
635
; If total number of Usages in all ranges is too small, duplicate the last
636
; Usage up to .count Usages (e.g. group of several indicators can have one usage
637
; "Generic Indicator" assigned to all fields).
638
	mov	ecx, [eax+report_field_group.count]
639
	mov	ebx, [usage_list]
640
.next_usage_range_for_variable:
641
	mov	edx, [ebx+usage_list_item.first_usage]
642
	push	[ebx+usage_list_item.num_usages]
643
.next_usage_for_variable:
644
	mov	[eax+report_field_group.common_sizeof], edx
645
	dec	ecx
646
	jz	@f
647
	add	eax, 4
648
	inc	edx
649
	dec	dword [esp]
650
	jnz	.next_usage_for_variable
651
	dec	edx
652
	inc	dword [esp]
653
	cmp	[ebx+usage_list_item.next], 0
654
	jz	.next_usage_for_variable
655
	pop	edx
656
	mov	ebx, [ebx+usage_list_item.next]
657
	jmp	.next_usage_range_for_variable
658
@@:
659
	pop	ebx
660
	jmp	.zero_local_items
661
.transform_usages_for_array:
662
; For Array field groups, leave ranges unexpanded, but recode in the form
663
; more convenient to value lookup, see comments in report_field_group structure.
664
	mov	ecx, [num_usage_ranges]
665
	mov	[eax+report_field_group.num_usage_ranges], ecx
666
	and	[eax+report_field_group.num_values_prev], 0
667
	mov	ecx, [usage_list]
668
	xor	ebx, ebx
669
@@:
670
	mov	edx, [ecx+usage_list_item.first_usage]
671
	mov	[eax+report_field_group.usages+usage_range.offset], ebx
672
	add	ebx, [ecx+usage_list_item.num_usages]
673
	jc	.invalid_report
674
	mov	[eax+report_field_group.usages+usage_range.first_usage], edx
675
	add	eax, sizeof.usage_range
676
	mov	ecx, [ecx+usage_list_item.next]
677
	test	ecx, ecx
678
	jnz	@b
679
	mov	[eax+report_field_group.usages], ebx
680
; New field is initialized.
681
	jmp	.zero_local_items
682
.padding:
683
	mov	[ebx+report.size], ecx
684
	jmp	.zero_local_items
685
; Create a new collection, nested in the current one.
686
.collection:
687
; Actions are quite straightforward:
688
; allocate, zero-initialize, update parent, if there is one,
689
; make it current.
690
	movi	eax, sizeof.collection
691
	call	Kmalloc
692
	test	eax, eax
693
	jz	.memory_error
694
	push	eax edi
695
	movi	ecx, sizeof.collection / 4
696
	xchg	edi, eax
697
	xor	eax, eax
698
	rep	stosd
699
	pop	edi eax
700
	mov	edx, [cur_collection]
701
	mov	[eax+collection.parent], edx
702
	lea	ecx, [last_collection]
703
	test	edx, edx
704
	jz	.no_parent
705
	lea	ecx, [edx+collection.last_child]
706
.no_parent:
707
	mov	edx, [ecx]
708
	mov	[ecx], eax
709
	mov	[edx], eax
710
	lea	ecx, [eax+collection.first_child]
711
; In theory, there must be at least one usage.
712
; In practice, some nested collections don't have any. Use zero in this case.
713
	mov	edx, [usage_list]
714
	test	edx, edx
715
	jz	@f
716
	mov	edx, [edx+usage_list_item.first_usage]
717
@@:
718
	mov	[eax+collection.last_child], ecx
719
	mov	[eax+collection.type], ebx
720
	mov	[eax+collection.usage], edx
721
	mov	[cur_collection], eax
722
	jmp	.zero_local_items
723
; Close the current collection.
724
.end_collection:
725
; There must be an opened collection.
726
	mov	eax, [cur_collection]
727
	test	eax, eax
728
	jz	.invalid_report
729
; Make parent collection the current one.
730
	mov	edx, [eax+collection.parent]
731
	mov	[cur_collection], edx
732
; Add field range of the closing collection to field range for nesting collection,
733
; if there is one.
734
	test	edx, edx
735
	jz	.zero_local_items
736
	push	3	; for each type: input, output, feature
737
.update_ranges:
738
	mov	ecx, [eax+collection.input.last_report]
739
	test	ecx, ecx
740
	jz	.no_fields
741
	mov	[edx+collection.input.last_report], ecx
742
	mov	ecx, [eax+collection.input.last_field]
743
	mov	[edx+collection.input.last_field], ecx
744
	cmp	[edx+collection.input.first_report], 0
745
	jnz	.no_fields
746
	mov	ecx, [eax+collection.input.first_report]
747
	mov	[edx+collection.input.first_report], ecx
748
	mov	ecx, [eax+collection.input.first_field]
749
	mov	[edx+collection.input.first_field], ecx
750
.no_fields:
751
	add	eax, sizeof.collection_report_set
752
	add	edx, sizeof.collection_report_set
753
	dec	dword [esp]
754
	jnz	.update_ranges
755
	pop	eax
756
	jmp	.zero_local_items
757
; ------------------------------- Global items --------------------------------
758
.parse_global:
759
	cmp	eax, .num_global_items
760
	jae	.item_parsed
761
	jmp	dword [.global_jumps+eax*4]
762
; For most global items, just store the value in the current global_items structure.
763
; Note 1: Usage Page will be used for upper word of Usage[| Minimum|Maximum], so
764
; shift it in advance.
765
; Note 2: the HID specification allows both signed and unsigned values for
766
; logical and physical minimum/maximum, but does not give a method to distinguish.
767
; Thus, hope that minimum comes first, parse the minimum as signed value always,
768
; if it is less than zero, assume signed values, otherwise assume unsigned values.
769
; This covers both common cases Minimum(0)/Maximum(FF) and Minimum(-7F)/Maximum(7F).
770
; Note 3: zero value for Report ID is forbidden by the HID specification.
771
; It is quite convenient, we use report_id == 0 for reports without ID.
772
.usage_page:
773
	shl	ebx, 16
774
	mov	[edi+global_items.usage_page], ebx
775
	jmp	.item_parsed
776
.logical_minimum:
777
	mov	[edi+global_items.logical_minimum], ecx
778
	jmp	.item_parsed
779
.logical_maximum:
780
	cmp	[edi+global_items.logical_minimum], 0
781
	jge	@f
782
	mov	ebx, ecx
783
@@:
784
	mov	[edi+global_items.logical_maximum], ebx
785
	jmp	.item_parsed
786
.physical_minimum:
787
	mov	[edi+global_items.physical_minimum], ecx
788
	jmp	.item_parsed
789
.physical_maximum:
790
	cmp	[edi+global_items.physical_maximum], 0
791
	jge	@f
792
	mov	ebx, ecx
793
@@:
794
	mov	[edi+global_items.physical_maximum], ebx
795
	jmp	.item_parsed
796
.unit_exponent:
797
	mov	[edi+global_items.unit_exponent], ecx
798
	jmp	.item_parsed
799
.unit:
800
	mov	[edi+global_items.unit], ebx
801
	jmp	.item_parsed
802
.report_size:
803
	mov	[edi+global_items.report_size], ebx
804
	jmp	.item_parsed
805
.report_id:
806
	test	ebx, ebx
807
	jz	.invalid_report
808
	cmp	ebx, 0x100
809
	jae	.invalid_report
810
	mov	[edi+global_items.report_id], ebx
811
	jmp	.item_parsed
812
.report_count:
813
	mov	[edi+global_items.report_count], ebx
814
	jmp	.item_parsed
815
; Two special global items: Push/Pop.
816
.push:
817
; For Push, allocate new global_items structure,
818
; initialize from the current one and make it current.
819
	movi	eax, sizeof.global_items
820
	call	Kmalloc
821
	test	eax, eax
822
	jz	.memory_error
823
	push	esi eax
824
	movi	ecx, sizeof.global_items / 4
825
	mov	esi, edi
826
	xchg	eax, edi
827
	rep	movsd
828
	pop	edi esi
829
	mov	[edi+global_items.next], eax
830
	jmp	.item_parsed
831
.pop:
832
; For Pop, restore the last global_items structure and free the current one.
833
	mov	eax, [edi+global_items.next]
834
	test	eax, eax
835
	jz	.invalid_report
836
	push	eax
837
	xchg	eax, edi
838
	call	Kfree
839
	pop	edi
840
	jmp	.item_parsed
841
; -------------------------------- Local items --------------------------------
842
.parse_local:
843
	cmp	eax, .num_local_items
844
	jae	.item_parsed
845
	jmp	dword [.local_jumps+eax*4]
846
.usage:
847
; Usage tag.
848
; If length is 0, 1, 2 bytes, append the global item Usage Page.
849
	cmp	[cur_item_size], 2
850
	ja	@f
851
	or	ebx, [edi+global_items.usage_page]
852
@@:
853
; If inside Delimiter(), ignore everything except the first tag.
854
	cmp	[delimiter_depth], 0
855
	jz	.usage.write
856
	inc	[usage_variant]
857
	cmp	[usage_variant], 1
858
	jnz	.item_parsed
859
.usage.write:
860
; Add new range with start = item data and length = 1.
861
	mov	[usage_minimum], ebx
862
	push	1
863
.new_usage:
864
	movi	eax, sizeof.usage_list_item
865
	call	Kmalloc
866
	pop	edx
867
	test	eax, eax
868
	jz	.memory_error
869
	inc	[num_usage_ranges]
870
	mov	ecx, [usage_minimum]
871
	and	[eax+usage_list_item.next], 0
872
	mov	[eax+usage_list_item.first_usage], ecx
873
	mov	[eax+usage_list_item.num_usages], edx
874
	mov	ecx, [usage_tail]
875
	mov	[usage_tail], eax
876
	mov	[ecx], eax
877
	jmp	.item_parsed
878
.usage_minimum:
879
; Usage Minimum tag. Just store in the local var.
880
; If length is 0, 1, 2 bytes, append the global item Usage Page.
881
	cmp	[cur_item_size], 2
882
	ja	@f
883
	or	ebx, [edi+global_items.usage_page]
884
@@:
885
	mov	[usage_minimum], ebx
886
	jmp	.item_parsed
887
.usage_maximum:
888
; Usage Maximum tag.
889
; If length is 0, 1, 2 bytes, append the global item Usage Page.
890
	cmp	[cur_item_size], 2
891
	ja	@f
892
	or	ebx, [edi+global_items.usage_page]
893
@@:
894
; Meaningless inside Delimiter().
895
	cmp	[delimiter_depth], 0
896
	jnz	.invalid_report
897
; Add new range with start = saved Usage Minimum and
898
; length = Usage Maximum - Usage Minimum + 1.
899
	sub	ebx, [usage_minimum]
900
	inc	ebx
901
	push	ebx
902
	jmp	.new_usage
903
.delimiter:
904
; Delimiter tag.
905
	test	ebx, ebx
906
	jz	.delimiter.close
907
; Delimiter(Opened).
908
; Store that we are inside Delimiter(),
909
; say a warning that only preferred Usage will be used.
910
	cmp	[delimiter_depth], 0
911
	jnz	.invalid_report
912
	inc	[delimiter_depth]
913
	push	esi
914
	mov	esi, delimiter_note
915
	call	SysMsgBoardStr
916
	pop	esi
917
	jmp	.item_parsed
918
.delimiter.close:
919
; Delimiter(Closed).
920
; Store that we are not inside Delimiter() anymore.
921
	dec	[delimiter_depth]
922
	js	.invalid_report
923
	and	[usage_variant], 0
924
	jmp	.item_parsed
925
.parse_reserved:
926
; Ignore reserved items, except that tag 0xFE means long item
927
; with first data byte = length of additional data,
928
; second data byte = long item tag. No long items are defined yet,
929
; so just skip them.
930
	cmp	eax, 0xF
931
	jnz	.item_parsed
932
	cmp	[cur_item_size], 2
933
	jnz	.item_parsed
934
	movzx	ecx, bl
935
	add	esi, ecx
936
	cmp	esi, [length]
937
	ja	.invalid_report
938
.item_parsed:
939
	cmp	esi, [length]
940
	jb	.fetch_next_item
941
.parse_end:
942
;-------------------------------- End of parsing ------------------------------
943
; If there are opened collections, it is invalid report.
944
	cmp	[cur_collection], 0
945
	jnz	.invalid_report
946
; There must be at least one input field.
947
	mov	eax, [calldata]
948
	add	eax, hid_data.input.first_report
949
	cmp	[last_reports+0*4], eax
950
	jz	.invalid_report
951
; Everything is ok.
952
	inc	[report_ok]
953
	jmp	.end
954
.memory_error:
955
	mov	esi, nomemory_msg
956
.end_str:
957
	call	SysMsgBoardStr
958
.end:
959
; Free all global_items structures.
960
	test	edi, edi
961
	jz	@f
962
	push	[edi+global_items.next]
963
	xchg	eax, edi
964
	call	Kfree
965
	pop	edi
966
	jmp	.end
967
@@:
968
; Free the last Usage list, if any.
969
	mov	eax, [usage_list]
970
@@:
971
	test	eax, eax
972
	jz	@f
973
	push	[eax+usage_list_item.next]
974
	call	Kfree
975
	pop	eax
976
	jmp	@b
977
@@:
978
}
979
 
980
; Assign drivers to top-level HID collections.
981
; The caller should provide ebx = pointer to hid_data and a local variable
982
; [has_driver], it will be initialized with 0 if no driver is present.
983
macro postprocess_descr
984
{
985
postprocess_report_label:
986
; Assign drivers to top-level collections.
987
; Use mouse driver for Usage(GenericDesktop:Mouse),
988
; use keyboard driver for Usage(GenericDesktop:Keyboard)
989
; and Usage(GenericDesktop:Keypad)
990
; 1. Prepare for the loop: get the pointer to the first collection,
991
; store that no drivers were assigned yet.
992
	mov	edi, [ebx+hid_data.first_collection]
993
if ~HID_DUMP_UNCLAIMED
994
	mov	[has_driver], 0
995
end if
996
.next_collection:
997
; 2. Test whether there is a collection to test; if no, break from the loop.
998
	test	edi, edi
999
	jz	.postprocess_done
1000
; 3. Get pointer to driver callbacks depending on [collection.usage].
1001
; If [collection.usage] is unknown, use default driver if HID_DUMP_UNCLAIMED
1002
; and do not assign a driver otherwise.
1003
	mov	esi, mouse_driver
1004
	cmp	[edi+collection.usage], USAGE_GD_MOUSE
1005
	jz	.has_driver
1006
	mov	esi, keyboard_driver
1007
	cmp	[edi+collection.usage], USAGE_GD_KEYBOARD
1008
	jz	.has_driver
1009
	cmp	[edi+collection.usage], USAGE_GD_KEYPAD
1010
	jz	.has_driver
1011
if HID_DUMP_UNCLAIMED
1012
	mov	esi, default_driver
1013
else
1014
	xor	esi, esi
1015
end if
1016
; 4. If no driver is assigned (possible only if not HID_DUMP_UNCLAIMED),
1017
; go to 7 with driver data = 0;
1018
; other code uses this as a sign that driver callbacks should not be called.
1019
.has_driver:
1020
	xor	eax, eax
1021
if ~HID_DUMP_UNCLAIMED
1022
	test	esi, esi
1023
	jz	.set_driver
1024
end if
1025
; 5. Notify the driver about new device.
1026
	call	[esi+hid_driver_callbacks.add_device]
1027
; 6. If the driver has returned non-zero driver data,
1028
; store that is an assigned driver.
1029
; Otherwise, if HID_DUMP_UNCLAIMED, try to assign the default driver.
1030
if HID_DUMP_UNCLAIMED
1031
	test	eax, eax
1032
	jnz	.set_driver
1033
	mov	esi, default_driver
1034
	call	[esi+hid_driver_callbacks.add_device]
1035
else
1036
	test	eax, eax
1037
	jz	@f
1038
	mov	[has_driver], 1
1039
	jmp	.set_driver
1040
@@:
1041
	xor	esi, esi
1042
end if
1043
.set_driver:
1044
; 7. Store driver data. If a driver is assigned, copy driver callbacks.
1045
	mov	[edi+collection.driver_data], eax
1046
	test	esi, esi
1047
	jz	@f
1048
	push	edi
1049
	lodsd	; skip hid_driver_callbacks.add_device
1050
	add	edi, collection.callbacks
1051
repeat sizeof.hid_driver_active_callbacks / 4
1052
	movsd
1053
end repeat
1054
	pop	edi
1055
@@:
1056
; 8. Store pointer to the collection in all input reports belonging to it.
1057
; Note that the HID spec requires that reports should not cross top-level collections.
1058
	mov	eax, [edi+collection.input.first_report]
1059
	test	eax, eax
1060
	jz	.reports_processed
1061
.next_report:
1062
	mov	[eax+report.top_level_collection], edi
1063
	cmp	eax, [edi+collection.input.last_report]
1064
	mov	eax, [eax+report.next]
1065
	jnz	.next_report
1066
.reports_processed:
1067
	mov	edi, [edi+collection.next]
1068
	jmp	.next_collection
1069
.postprocess_done:
1070
}
1071
 
1072
; Cleanup all resources allocated during parse_descr and postprocess_descr.
1073
; Called when the corresponding device is disconnected
1074
; with ebx = pointer to hid_data.
1075
macro hid_cleanup
1076
{
1077
; 1. Notify all assigned drivers about disconnect.
1078
; Loop over all top-level collections and call callbacks.disconnect,
1079
; if a driver is assigned.
1080
	mov	esi, [ebx+hid_data.first_collection]
1081
.notify_drivers:
1082
	test	esi, esi
1083
	jz	.notify_drivers_done
1084
	mov	edi, [esi+collection.driver_data]
1085
	test	edi, edi
1086
	jz	@f
1087
	call	[esi+collection.callbacks.disconnect]
1088
@@:
1089
	mov	esi, [esi+collection.next]
1090
	jmp	.notify_drivers
1091
.notify_drivers_done:
1092
; 2. Free all collections.
1093
	mov	esi, [ebx+hid_data.first_collection]
1094
.free_collections:
1095
	test	esi, esi
1096
	jz	.collections_done
1097
; If a collection has childen, make it forget about them,
1098
; kill all children; after last child is killed, return to
1099
; the collection as a parent; this time, it will appear
1100
; as childless, so it will be killed after children.
1101
	mov	eax, [esi+collection.first_child]
1102
	test	eax, eax
1103
	jz	.no_children
1104
	and	[esi+collection.first_child], 0
1105
	xchg	esi, eax
1106
	jmp	.free_collections
1107
.no_children:
1108
; If a collection has no children (maybe there were no children at all,
1109
; maybe all children were already killed), kill it and proceed either to
1110
; next sibling (if any) or to the parent.
1111
	mov	eax, [esi+collection.next]
1112
	test	eax, eax
1113
	jnz	@f
1114
	mov	eax, [esi+collection.parent]
1115
@@:
1116
	xchg	eax, esi
1117
	call	Kfree
1118
	jmp	.free_collections
1119
.collections_done:
1120
; 3. Free all three report sets.
1121
	push	3
1122
	lea	esi, [ebx+hid_data.input]
1123
; For every report set, loop over all reports,
1124
; for every report free all field groups, then free report itself.
1125
; When all reports in one set have been freed, free also report list table,
1126
; if there is one (reports are numbered).
1127
.report_set_loop:
1128
	mov	edi, [esi+report_set.first_report]
1129
.report_loop:
1130
	test	edi, edi
1131
	jz	.report_done
1132
	mov	eax, [edi+report.first_field]
1133
.field_loop:
1134
	test	eax, eax
1135
	jz	.field_done
1136
	push	[eax+report_field_group.next]
1137
	call	Kfree
1138
	pop	eax
1139
	jmp	.field_loop
1140
.field_done:
1141
	mov	eax, [edi+report.next]
1142
	xchg	eax, edi
1143
	call	Kfree
1144
	jmp	.report_loop
1145
.report_done:
1146
	cmp	[esi+report_set.numbered], 0
1147
	jz	@f
1148
	mov	eax, [esi+report_set.data]
1149
	call	Kfree
1150
@@:
1151
	add	esi, sizeof.report_set
1152
	dec	dword [esp]
1153
	jnz	.report_set_loop
1154
	pop	eax
1155
}
1156
 
1157
; Helper for parse_input. Extracts value of one field.
1158
; in: esi -> report_field_group
1159
; in: eax = offset in bits from report start
1160
; in: report -> report data
1161
; out: edx = value
1162
; Note: it can read one dword past report data.
1163
macro extract_field_value report
1164
{
1165
	mov	ecx, eax
1166
	shr	eax, 5
1167
	shl	eax, 2
1168
	add	eax, report
1169
	and	ecx, 31
1170
	mov	edx, [eax]
1171
	mov	eax, [eax+4]
1172
	shrd	edx, eax, cl
1173
	mov	ecx, [esi+report_field_group.sign_mask]
1174
	and	ecx, edx
1175
	and	edx, [esi+report_field_group.mask]
1176
	sub	edx, ecx
1177
}
1178
 
1179
; Local variables for parse_input.
1180
macro parse_input_locals
1181
{
1182
count_inside_group	dd	?
1183
; Number of fields left in the current field.
1184
field_offset		dd	?
1185
; Offset of the current field from report start, in bits.
1186
field_range_size		dd	?
1187
; Size of range with valid values, Logical Maximum - Logical Minimum + 1.
1188
cur_usage		dd	?
1189
; Pointer to current usage for Variable field groups.
1190
num_values		dd	?
1191
; Number of values in the current instantiation of Array field group.
1192
values_base		dd	?
1193
; Pointer to memory allocated for array with current values.
1194
values_prev		dd	?
1195
; Pointer to memory allocated for array with previous values.
1196
values_cur_ptr		dd	?
1197
; Pointer to the next value in [values_base] array.
1198
values_end		dd	?
1199
; End of data in array with current values.
1200
values_prev_ptr		dd	?
1201
; Pointer to the next value in [values_prev_ptr] array.
1202
values_prev_end		dd	?
1203
; End of data in array with previous values.
1204
}
1205
 
1206
; Parse input report. The caller should provide esi = pointer to report,
1207
; local variables parse_input_locals and [buffer] = report data.
1208
macro parse_input
1209
{
1210
; 1. Ignore the report if there is no driver for it.
1211
	mov	ebx, [esi+report.top_level_collection]
1212
	mov	edi, [ebx+collection.driver_data]
1213
	test	edi, edi
1214
	jz	.done
1215
; 2. Notify the driver that a new packet arrived.
1216
	call	[ebx+collection.callbacks.begin_packet]
1217
; Loop over all field groups.
1218
; Report without fields is meaningless, but theoretically possible:
1219
; parse_descr does not create reports of zero size, but
1220
; a report can consist of "padding" fields without usages and have
1221
; no real fields.
1222
	mov	esi, [esi+report.first_field]
1223
	test	esi, esi
1224
	jz	.packet_processed
1225
.field_loop:
1226
; 3. Prepare for group handling: initialize field offset, fields count
1227
; and size of range for valid values.
1228
	mov	eax, [esi+report_field_group.offset]
1229
	mov	[field_offset], eax
1230
	mov	ecx, [esi+report_field_group.count]
1231
	mov	[count_inside_group], ecx
1232
	mov	eax, [esi+report_field_group.logical_maximum]
1233
	inc	eax
1234
	sub	eax, [esi+report_field_group.logical_minimum]
1235
	mov	[field_range_size], eax
1236
; 4. Select handler. Variable and Array groups are handled entirely differently;
1237
; for Variable groups, advance to 5, for Array groups, go to 6.
1238
	test	byte [esi+report_field_group.flags], HID_FIELD_VARIABLE
1239
	jz	.array_field
1240
; 5. Variable groups. They are simple. Loop over all .count fields,
1241
; for every field extract the value and get the next usage,
1242
; if the value is within valid range, call the driver.
1243
	lea	eax, [esi+report_field_group.common_sizeof]
1244
	mov	[cur_usage], eax
1245
.variable_data_loop:
1246
	mov	eax, [field_offset]
1247
	extract_field_value [buffer]	; -> edx
1248
	mov	ecx, [cur_usage]
1249
	mov	ecx, [ecx]
1250
	call	[ebx+collection.callbacks.input_field]
1251
	add	[cur_usage], 4
1252
	mov	eax, [esi+report_field_group.size]
1253
	add	[field_offset], eax
1254
	dec	[count_inside_group]
1255
	jnz	.variable_data_loop
1256
; Variable group is processed; go to 12.
1257
	jmp	.field_done
1258
.array_field:
1259
; Array groups. They are complicated.
1260
; 6. Array group: extract all values in one array.
1261
; memory was allocated during group creation, use it
1262
; 6a. Prepare: get data pointer, initialize num_values with zero.
1263
	mov	eax, [esi+report_field_group.num_usage_ranges]
1264
	lea	edx, [esi+report_field_group.usages+eax*sizeof.usage_range+4]
1265
	mov	eax, [esi+report_field_group.count]
1266
	mov	[values_cur_ptr], edx
1267
	mov	[values_base], edx
1268
	lea	edx, [edx+ecx*4]
1269
	mov	[values_prev], edx
1270
	mov	[values_prev_ptr], edx
1271
	mov	[num_values], 0
1272
; 6b. Start loop for every field. Note that there must be at least one field,
1273
; parse_descr does not allow .count == 0.
1274
.array_getval_loop:
1275
; 6c. Extract the value of the current field.
1276
	mov	eax, [field_offset]
1277
	extract_field_value [buffer]	; -> edx
1278
; 6d. Transform the value to the usage with binary search in array of
1279
; usage_ranges. started at [esi+report_field_group.usages]
1280
; having [esi+report_field_group.num_usage_ranges] items.
1281
; Ignore items outside of valid range.
1282
	sub	edx, [esi+report_field_group.logical_minimum]
1283
	cmp	edx, [field_range_size]
1284
	jae	.array_skip_item
1285
; If there are too few usages, use last of them.
1286
	mov	ecx, [esi+report_field_group.num_usage_ranges]	; upper bound
1287
	xor	eax, eax	; lower bound
1288
	cmp	edx, [esi+report_field_group.usages+ecx*sizeof.usage_range+usage_range.offset]
1289
	jae	.array_last_usage
1290
; loop invariant: usages[eax].offset <= edx < usages[ecx].offset
1291
.array_find_usage:
1292
	lea	edi, [eax+ecx]
1293
	shr	edi, 1
1294
	cmp	edi, eax
1295
	jz	.array_found_usage_range
1296
	cmp	edx, [esi+report_field_group.usages+edi*sizeof.usage_range+usage_range.offset]
1297
	jae	.update_low
1298
	mov	ecx, edi
1299
	jmp	.array_find_usage
1300
.update_low:
1301
	mov	eax, edi
1302
	jmp	.array_find_usage
1303
.array_last_usage:
1304
	lea	eax, [ecx-1]
1305
	mov	edx, [esi+report_field_group.usages+ecx*sizeof.usage_range+usage_range.offset]
1306
	dec	edx
1307
.array_found_usage_range:
1308
	sub	edx, [esi+report_field_group.usages+eax*sizeof.usage_range+usage_range.offset]
1309
	add	edx, [esi+report_field_group.usages+eax*sizeof.usage_range+usage_range.first_usage]
1310
; 6e. Store the usage, advance data pointer, continue loop started at 6b.
1311
	mov	eax, [values_cur_ptr]
1312
	mov	[eax], edx
1313
	add	[values_cur_ptr], 4
1314
	inc	[num_values]
1315
.array_skip_item:
1316
	mov	eax, [esi+report_field_group.size]
1317
	add	[field_offset], eax
1318
	dec	[count_inside_group]
1319
	jnz	.array_getval_loop
1320
; 7. Array group: ask driver about array overflow.
1321
; If driver says that the array is invalid, stop processing this group
1322
; (in particular, do not update previous values).
1323
	mov	ecx, [num_values]
1324
	test	ecx, ecx
1325
	jz	.duplicates_removed
1326
	mov	edx, [values_base]
1327
	mov	edi, [ebx+collection.driver_data]
1328
	call	[ebx+collection.callbacks.array_overflow?]
1329
	jnc	.field_done
1330
; 8. Array group: sort the array with current values.
1331
	push	esi
1332
	mov	ecx, [num_values]
1333
	mov	edx, [values_base]
1334
	call	sort
1335
	pop	esi
1336
; 9. Array group: remove duplicates.
1337
	cmp	[num_values], 1
1338
	jbe	.duplicates_removed
1339
	mov	eax, [values_base]
1340
	mov	edx, [eax]
1341
	add	eax, 4
1342
	mov	ecx, eax
1343
.duplicates_loop:
1344
	cmp	edx, [eax]
1345
	jz	@f
1346
	mov	edx, [eax]
1347
	mov	[ecx], edx
1348
	add	ecx, 4
1349
@@:
1350
	add	eax, 4
1351
	cmp	eax, [values_cur_ptr]
1352
	jb	.duplicates_loop
1353
	mov	[values_cur_ptr], ecx
1354
	sub	ecx, [values_base]
1355
	shr	ecx, 2
1356
	mov	[num_values], ecx
1357
.duplicates_removed:
1358
; 10. Array group: compare current and previous values,
1359
; call driver for differences.
1360
	mov	edi, [ebx+collection.driver_data]
1361
	mov	eax, [values_cur_ptr]
1362
	mov	[values_end], eax
1363
	mov	eax, [values_base]
1364
	mov	[values_cur_ptr], eax
1365
	mov	eax, [esi+report_field_group.num_values_prev]
1366
	shl	eax, 2
1367
	add	eax, [values_prev]
1368
	mov	[values_prev_end], eax
1369
.find_common:
1370
	mov	eax, [values_cur_ptr]
1371
	cmp	eax, [values_end]
1372
	jae	.cur_done
1373
	mov	ecx, [eax]
1374
	mov	eax, [values_prev_ptr]
1375
	cmp	eax, [values_prev_end]
1376
	jae	.prev_done
1377
	mov	edx, [eax]
1378
	cmp	ecx, edx
1379
	jb	.advance_cur
1380
	ja	.advance_prev
1381
; common item in both arrays; ignore
1382
	add	[values_cur_ptr], 4
1383
	add	[values_prev_ptr], 4
1384
	jmp	.find_common
1385
.advance_cur:
1386
; item is present in current array but not in previous;
1387
; call the driver with value = 1
1388
	add	[values_cur_ptr], 4
1389
	mov	edx, 1
1390
	call	[ebx+collection.callbacks.input_field]
1391
	jmp	.find_common
1392
.advance_prev:
1393
; item is present in previous array but not in current;
1394
; call the driver with value = 0
1395
	add	[values_prev_ptr], 4
1396
	mov	ecx, edx
1397
	xor	edx, edx
1398
	call	[ebx+collection.callbacks.input_field]
1399
	jmp	.find_common
1400
.prev_done:
1401
; for all items which are left in current array
1402
; call the driver with value = 1
1403
	mov	eax, [values_cur_ptr]
1404
@@:
1405
	add	[values_cur_ptr], 4
1406
	mov	ecx, [eax]
1407
	mov	edx, 1
1408
	call	[ebx+collection.callbacks.input_field]
1409
	mov	eax, [values_cur_ptr]
1410
	cmp	eax, [values_end]
1411
	jb	@b
1412
	jmp	.copy_array
1413
.cur_done:
1414
; for all items which are left in previous array
1415
; call the driver with value = 0
1416
	mov	eax, [values_prev_ptr]
1417
	add	[values_prev_ptr], 4
1418
	cmp	eax, [values_prev_end]
1419
	jae	@f
1420
	mov	ecx, [eax]
1421
	xor	edx, edx
1422
	call	[ebx+collection.callbacks.input_field]
1423
	jmp	.cur_done
1424
@@:
1425
.copy_array:
1426
; 11. Array group: copy current values to previous values.
1427
	push	esi edi
1428
	mov	ecx, [num_values]
1429
	mov	[esi+report_field_group.num_values_prev], ecx
1430
	mov	esi, [values_base]
1431
	mov	edi, [values_prev]
1432
	rep	movsd
1433
	pop	edi esi
1434
; 12. Field group is processed. Repeat with the next group, if any.
1435
.field_done:
1436
	mov	esi, [esi+report_field_group.next]
1437
	test	esi, esi
1438
	jnz	.field_loop
1439
.packet_processed:
1440
; 13. Packet is processed, notify the driver.
1441
	call	[ebx+collection.callbacks.end_packet]
1442
}