-
Notifications
You must be signed in to change notification settings - Fork 0
/
usbnew.spin2
4517 lines (4199 loc) · 207 KB
/
usbnew.spin2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{{
"USBNEW" modular USB input driver - v1.1.5
By Ada Gottensträter ("Wuerfel_21")
Original 1CogKbM code by Garry Jordan ("garryj")
Hub support and significant portions by Marco Maccaferri ("macca")
TERMS OF USE: MIT License
}}
CON
DEBUG_DISABLE = true
CLKFREQ_PTR = $14
USB_BASE_PIN = 16
USB_ENABLE_OFFSET = 1 ' Set -1 if not present
USB_DMINUS_OFFSET = 2 ' Must end up on an even pin. D+ is always this +1
ACTIVITY_LED = -1
ERROR_LED = -1
OVERLAP_MEMORY = false ' save memory at cost of making the driver non-reentrant
KEYQUEUE_SIZE = 0
NUMLOCK_DEFAULT_STATE = true
RPI_KEYBOARD_NUMLOCK_HACK = true
HAVE_MOUSE = false
MOUSE_FULL_PROTOCOL = false ' Experimental: Use full HID report protocol for mice
HAVE_HIDPAD = false ' Set to false to disable HidPad inteface
HIDPAD_REPORT_SIZE = 8*4 ' do not change!
EMUPAD_MAX_PLAYER = 0 ' Set to 0 to disable EmuPad interface
EMUPAD_MAX_RULES = 32
EMUPAD_BUILTIN_RULES = false
' EmuPad report format config
EMUPAD_TYPE_NIBBLE = 7
EMUPAD_TYPE_KBD = 1
EMUPAD_TYPE_PAD = 2
EMUPAD_TYPE_PAD_3BT = 2 ' MegaYume hack (also causes error when trying to link unmodified usbnew)
EMUPAD_UP_BIT = 0
EMUPAD_DOWN_BIT = 1
EMUPAD_LEFT_BIT = 2
EMUPAD_RIGHT_BIT = 3
EMUPAD_BT0_BIT = 4
EMUPAD_BT1_BIT = 5
EMUPAD_BT2_BIT = 6
EMUPAD_BT3_BIT = 7
EMUPAD_BT4_BIT = 8
EMUPAD_BT5_BIT = 9
EMUPAD_BT6_BIT = 10
EMUPAD_BT7_BIT = 11
EMUPAD_BT8_BIT = 12
EMUPAD_BT9_BIT = 13
' EmuPad default keyboard map
' You should probably override these
EMUPAD_KB_UP = KEY_UP
EMUPAD_KB_DOWN = KEY_DOWN
EMUPAD_KB_LEFT = KEY_LEFT
EMUPAD_KB_RIGHT = KEY_RIGHT
EMUPAD_KB_BT0 = KEY_D
EMUPAD_KB_BT1 = KEY_V
EMUPAD_KB_BT2 = KEY_C
EMUPAD_KB_BT3 = KEY_X
EMUPAD_KB_BT4 = KEY_F
EMUPAD_KB_BT5 = KEY_G
EMUPAD_KB_BT6 = KEY_T
EMUPAD_KB_BT7 = KEY_Y
EMUPAD_KB_BT8 = KEY_BACKSPACE
EMUPAD_KB_BT9 = KEY_ENTER
' Computed magic values
_EMUPAD_UD_MASK = (decod EMUPAD_UP_BIT) | (decod EMUPAD_DOWN_BIT)
_EMUPAD_LR_MASK = (decod EMUPAD_LEFT_BIT) | (decod EMUPAD_RIGHT_BIT)
_EMUPAD_RULE_SIZE = 10+4+1
_HAVE_GAMEPAD = HAVE_HIDPAD || (EMUPAD_MAX_PLAYER > 0)
MAX_DEVICES = 7 ' DO. NOT. TOUCH.
OBJ '' Startup/configuration
PUB start() : r
ifnot driver_cog
r := coginit(COGEXEC_NEW,@usb_host_start,0)
if r >= 0
driver_cog := r+1
PUB stop()
if driver_cog
cogstop(driver_cog-1)
ifnot OVERLAP_MEMORY ' if OVERLAP_MEMORY, be careful
driver_cog := 0
pinclear(dm addpins 1)
if usb_enable_pin >= 0
pinclear(usb_enable_pin)
'' Get current driver cog or -1 if not running
PUB current_cog() : r
return driver_cog - 1
'' Must be called before start()
PUB set_basepin(pin)
if USB_ENABLE_OFFSET >= 0
usb_enable_pin := pin+USB_ENABLE_OFFSET
dm := pin+USB_DMINUS_OFFSET
dp := pin+USB_DMINUS_OFFSET+1
'' Must be called before start()
PUB set_pins_explicit(enable,dminus)
usb_enable_pin := enable
dm := dminus
dp := dminus+1
OBJ '' Keyboard State API
'' (Always enabled)
'' Get current pressed state of a keyboard key
PUB keystate(key) : r | buf[2],tmp
if key >= $E0 && key < $E8
return not not (hkbd_report.byte[0] >> (key-$E0))
else
'' Do this in inline ASM to ensure atomicity
tmp := @hkbd_report
org
setq #1
rdlong buf,tmp
getbyte tmp,buf+0,#2
cmp tmp,key wz
getbyte tmp,buf+0,#3
if_nz cmp tmp,key wz
getbyte tmp,buf+1,#0
if_nz cmp tmp,key wz
getbyte tmp,buf+1,#1
if_nz cmp tmp,key wz
getbyte tmp,buf+1,#2
if_nz cmp tmp,key wz
getbyte tmp,buf+1,#3
if_nz cmp tmp,key wz
if_z neg r,#1
end
PUB ledstates() : r
return hkbd_led_rep
OBJ '' Keyboard queue API
'' Set KEYQUEUE_SIZE to enable (recommended value: 32)
'' Return key event from buffer if there is one
PUB get_key() : r | tail
tail := keyq_tail
if keyq_head == tail
return 0 ' No event
r := keyq_data[tail]
if ++tail == KEYQUEUE_SIZE
tail := 0
keyq_tail := tail
'' Peek top of key event buffer (returns 0 if no pending event)
PUB peek_key() : r | tail
tail := keyq_tail
if keyq_head == tail
return 0 ' No event
else
return keyq_data[tail]
OBJ '' Mouse API
'' Set HAVE_MOUSE to enable
'' Get mouse coordinates
PUB mouse_xy() : x,y
x := mouse_xacc
y := mouse_yacc
'' Get mouse scrolling (horizontal scroll NYI)
PUB mouse_scroll() : x,y
x := 0
y := mouse_zacc
'' Emulate mouse movement
PUB mouse_move(x,y)
mouse_lock := 1
waitus(2) ' hope driver is now done
mouse_xacc := x
mouse_yacc := y
if mouse_outptr
long[mouse_outptr] := y << 16 | x & $FFFF
mouse_lock := 0
'' Get mouse buttons state
PUB mouse_buttons() : r
return mouse_bstate
'' Set limits for mouse movement
PUB mouse_set_limits(xlim,ylim)
mouse_limits[0] := xlim
mouse_limits[1] := ylim
'' Set pointer for "hardware" sprite control
PUB mouse_set_outptr(ptr)
mouse_outptr := ptr
OBJ '' HIDPad API
'' Set HAVE_HIDPAD to enable.
'' Warning: never pass a device ID larger or equal than MAX_DEVICES
'' Get VID/PID pair for connected device. 0 means disconnected
PUB hidpad_id(dev) : r
return long[@hidpad_report+dev*HIDPAD_REPORT_SIZE][0]
'' Get device capabilites. These are often false-positive.
'' (i.e. axis marked as valid, but not physically extant on device)
'' xxxxxxxx_wsrrrzyx_hhhhaaaa_bbbbbbbb
'' hlzyx||| |||||||| ++++++++ n. buttons
'' |||||||| ||||++++--------- n. axes
'' |||||||| ++++------------- n. hats
'' ++++++++------------------ valid axes
PUB hidpad_getcaps(dev) : r
return long[@hidpad_report+dev*HIDPAD_REPORT_SIZE][1]
'' Get all button states for a device
PUB hidpad_buttons(dev) : r
return long[@hidpad_report+dev*HIDPAD_REPORT_SIZE][6]
'' Get state of one button
PUB hidpad_button(dev,bnum) : r
if bnum +>= 32
return false
else
return long[@hidpad_report+dev*HIDPAD_REPORT_SIZE][6].[bnum]
'' Get state of an axis (normalized between -32768 and 32767)
PUB hidpad_axis(dev,axnum) : r
if axnum +>= 8
return 0
else
return word[@hidpad_report+dev*HIDPAD_REPORT_SIZE][4+axnum] signx 15
'' Get state of a hat
PUB hidpad_hat(dev,hatnum) : r
if hatnum +>= 4
return 0
else
return byte[@hidpad_report+dev*HIDPAD_REPORT_SIZE][7*4+hatnum]
OBJ '' EmuPad API
'' Set EMUPAD_MAX_PLAYER to enable
'' See docs for more details
PUB set_emupad_ptr(ptr) ' Call before starting!
if EMUPAD_MAX_PLAYER > 0
emupad_out_ptr := ptr
PUB get_emupad_rule_buffer() : r
return @emupad_rule_buffer
OBJ '' Raw buffer getters (for advanced/debug use)
PUB get_keyboard_raw_buffer() :r
return @hkbd_report
PUB get_hidpad_buffer() : r
return @hidpad_report
DAT ' Startup for ASM only
{
org $000
asmclk
drvl #56
drvl #57
coginit #0, ##@usb_host_start
}
DAT ' USB Host driver
org $000
usb_host_start
mov hcog_base_addr, ptrb
mov htmp, ##@hlut_end - 4 - @hlut_start ' Dealing with hub addresses
shr htmp, #2 ' so byte->long for the lut cell count
loc pb, #@hlut_start - @usb_host_start
add pb, hcog_base_addr
setq2 htmp
rdlong 0, pb ' Do the hub->lut copy
loc pb, #@usb_host_init - @usb_host_start
add pb, hcog_base_addr
jmp pb ' Initialize host and enter main processing loop
'------------------------------------------------------------------------------
' SETUP transaction. The mechanics of SETUP are identical to OUT, but it's
' special because the receiving function must not respond with either STALL or
' NAK, and must accept the DATAx packet that follows the SETUP token. If a
' non-control endpoint receives a SETUP token, or the function receives a
' corrupt packet, it must ignore the transaction
'------------------------------------------------------------------------------
' On entry:
' PTRA - start address of the SETUP data struct.
' On exit:
' retval - PID_ACK on success, otherwise error code.
'------------------------------------------------------------------------------
txn_setup
setbyte ep_addr_pid, #PID_SETUP, #0
mov pkt_data, #SETUP_TXN_LEN ' SETUP is single fixed size DATAx packet
bitl hstatus, #DATAx_TGLB ' And always uses DATA0 packet
mov retry, #TXN_RETRIES ' Retries possible as function will ignore a corrupt packet
mov pa, ptra ' Save SETUP struct pointer in case of retry
.setup
call #txn_out ' SETUP/OUT are the same transaction type, just different PIDs
cmp retval, #PID_ACK wz
if_z ret
call #retry_wait
cmp retval, #ERR_TXN_RETRY wz
if_z ret
mov ptra, pa ' Restore SETUP's DATAx pointer
jmp #.setup
'------------------------------------------------------------------------------
' IN/INTERRUPT transaction.
' Possible function response: STALL or NAK handshake, or DATAx packet.
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - PID_IN(b0..7), address(b8..b14), endpoint(b15..18) and
' CRC(b19..23).
' On exit:
'------------------------------------------------------------------------------
txn_in
call #wait_txn_ok ' ISR: ensure txn doesn't cross frame boundary
setbyte ep_addr_pid, #PID_IN, #0
call #utx_token ' Put IN request on the bus
' Fall through to urx_packet
'------------------------------------------------------------------------------
' Wait for a packet from a device/function. As host, the only two packet types
' received are handshakes and IN DATAx.
'------------------------------------------------------------------------------
' On entry:
' On exit:
' retval - the ID of the packet. If a PID fails validation, ERR_PACKET is
' returned.
'------------------------------------------------------------------------------
urx_packet
rqpin urx, dm ' Wait until start-of-packet signal appears on the USB.
testb urx, #SOPB wc
if_c jmp #urx_packet
getct hct2
addct2 hct2, tat_wait ' Start the response turn-around timer
bitl hstatus, #EOPB ' Make sure sticky EOP flag is clear
mov newb_flg, #0 ' Initialize for multi-byte read
.wait_sop
rdpin urx, dm
testb urx, #SOPB wc
if_c jmp #.get_pid
jnct2 #.wait_sop
_ret_ mov retval, #ERR_TAT
.get_pid
call #urx_next
testb urx, #BUS_ERRB wc
if_nc jmp #.chk_pid
_ret_ mov retval, #ERR_URX
.chk_pid
cmp retval, #PID_ACK wz
if_nz cmp retval, #PID_NAK wz
if_nz cmp retval, #PID_STALL wz
if_z jmp #.chk_eop ' Handshake, so check that packet is single byte
testb hstatus, #DATAx_TGLB wc ' Get low/full speed even/odd DATAx sequence to look for
cmp retval, #PID_DATA0 wz
if_z_and_nc jmp #urx_data ' DATA0 and sequence match
if_z_and_c jmp #.ack_resend ' Sequence error. Ignore data, resend the ACK that the device must have missed
cmp retval, #PID_DATA1 wz
if_z_and_c jmp #urx_data ' DATA1 and sequence match
if_z_and_nc jmp #.ack_resend
_ret_ mov retval, #ERR_PACKET ' Some other bus error...
.ack_resend
rqpin urx, dm
testb urx, #EOPB wc
if_nc jmp #.ack_resend
mov retval, #PID_ACK
call #utx_handshake ' Send handshake PID and return to caller
_ret_ mov retval, #ERR_DATAX_SYNC
.chk_eop
testb hstatus, #LOW_SPEEDB wc
if_nc jmp #.idle ' Full-speed doesn't need an additional read to get EOP status
call #urx_next ' Low-speed requires an additional read to get EOP status
testb hstatus, #EOPB wc
if_c jmp #.idle ' Low-speed EOP seen
testb urx, #BUS_ERRB wz
if_nc mov retval, #ERR_PACKET ' No EOP where one was expected
if_z mov retval, #ERR_URX ' Bit unstuff error, EOP SE0 > 3 bits or SE1, so we're hosed
ret
.idle
rqpin urx, dm
testb urx, #J_IDLEB wc
if_nc jmp #.idle ' Wait for bus IDLE before returning handshake result
ret
'------------------------------------------------------------------------------
' Send a token packet with CRC5 checksum of address and endpoint. It is the
' responsibility of the caller to append the appropriate inter-packet delay,
' if one is required.
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - packed with the PID, address and endpoint.
' On exit:
'------------------------------------------------------------------------------
utx_token
rqpin urx, dm
testb urx, #J_IDLEB wc
if_nc jmp #utx_token
testb hstatus, #DWNSTRM_HUBB wc
if_c call #utx_pre
mov utx, #OUT_SOP
call #utx_byte ' Send sync byte
mov htmp, ep_addr_pid ' Preserve the PID and destination
mov pkt_cnt, #3
.next_byte
getbyte utx, htmp, #0 ' Bytes on the bus LSB->MSB
shr htmp, #8 ' Shift to next byte to send
.wait
testp dp wc
if_nc jmp #.wait
akpin dp
wypin utx, dm
_ret_ djnz pkt_cnt, #.next_byte
'------------------------------------------------------------------------------
' SETUP/OUT/INTERRUPT transaction.
' Possible function response in order of precedence: STALL, ACK, NAK.
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - PID_OUT(b0..7), address(b8..b14), endpoint(b15..18) and
' CRC(b19..23).
' PTRA - start address of the data buff/struct that has the bytes to send.
' pkt_data - count of DATAx payload bytes to send.
' On exit:
'------------------------------------------------------------------------------
txn_out
call #wait_txn_ok ' ISR: ensure txn doesn't cross frame boundary
call #utx_token ' Put SETUP/OUT token on the bus
rdfast ##$80000000, ptra ' Use hub RAM FIFO interface to read the tx buffer
mov pkt_cnt, pkt_data
' Fall through to utx_data
'------------------------------------------------------------------------------
' Transmit a DATAx packet with USB-16 checksum of payload. The payload CRC is
' calculated while the data byte is being shifted out. Since data stage
' success/fail is not determined until the status stage of the transaction,
' this routine is only concerned about the current DATAx packet.
'------------------------------------------------------------------------------
' On entry:
' PTRA - hub start address of the data to read.
' pkt_cnt - data payload size.
' On exit:
'------------------------------------------------------------------------------
utx_data
rqpin urx, dm
testb urx, #SOPB wc
if_c jmp #utx_data
mov hctwait, ip_delay
call #poll_waitx ' SETUP/OUT token always precedes tx DATAx so insert IP delay
testb hstatus, #DWNSTRM_HUBB wc
if_c call #utx_pre
mov utx, #OUT_SOP
call #utx_byte ' Send sync
bmask crc, #15 ' Prime the CRC16 pump
testb hstatus, #DATAx_TGLB wc ' Set the requested DATAx PID
if_nc mov utx, #PID_DATA0
if_c mov utx, #PID_DATA1
call #utx_byte ' No CRC calc done on PID
cmp pkt_cnt, #0 wz ' Check if sending a zero length payload
if_z jmp #.send_crc ' If so, only the CRC goes out
.read_byte
rfbyte utx ' Fetch data byte
call #utx_byte
rev utx ' Calculate CRC while the data is shifting out
setq utx ' SETQ left-justifies the reflected data byte
crcnib crc, usb16_poly_r ' Run CRC calc on the data nibs
crcnib crc, usb16_poly_r
djnz pkt_cnt, #.read_byte
.send_crc
bitnot crc,#0 addbits 15 ' Final XOR, and send the calculated CRC16
getbyte utx, crc, #0
call #utx_byte
getbyte utx, crc, #1
call #utx_byte ' Last CRC byte out
jmp #urx_packet ' Handle function response/error and back to caller
'------------------------------------------------------------------------------
' Receive a DATAx_ payload with USB-16 checksum. The CRC is calculated as the
' payload bytes are received. The routine reads bytes until EOP is detected and
' expects that the packet includes at least the CRC word.
'
' In control transfers, it's possible to recieve fewer data bytes than what
' was requested, which makes it difficult to determine where the data stops
' and the CRC word begins. So the CRC calculation is done on every byte of the
' packet, including the CRC word. The CRC value should then be equal to the
' USB-16 expected residual value of 0xB001.
'
' The routine writes the IN packet data to a static max_packet_size buffer
' so the caller can verify IN success before writing the data to its final
' destination.
'------------------------------------------------------------------------------
' On entry:
' pkt_data - max byte count expected to be in the packet.
' newb_flg - signals new byte ready when toggled.
' On exit:
' pkt_cnt - actual number of bytes read.
'------------------------------------------------------------------------------
urx_data
mov htmp2, pb
mov pb, urx_buff_p
wrfast ##$80000000, pb ' Use hub RAM FIFO interface to buffer bytes received
mov pb, htmp2
bmask crc, #15 ' Prime the CRC16 pump
mov pkt_cnt, #0 ' Keep track of payload bytes received
mov pkt_tmp, pkt_data
add pkt_tmp, #2 ' Tweak payload byte count to include CRC word
.wait_byte
' In-line rx for max speed
rqpin urx, dm
mov utx, #BYTE_TGLF ' Reg utx free in this context
and utx, urx
cmp newb_flg, utx wz ' Fetch a byte whenever the flags differ
if_nz xor newb_flg, #BYTE_TGLF ' Synchronize flags
if_nz jmp #.get_byte ' New byte!
testb urx, #EOPB wc
if_c jmp #.chk_crc ' At end-of-packet
jmp #.wait_byte
.get_byte
getbyte retval, urx, #1 ' New byte from smart pins
wfbyte retval ' Add it to the data buffer
rev retval ' Calculate CRC while next byte is shifting in
setq retval ' SETQ left-justifies the reflected data byte
crcnib crc, usb16_poly_r ' Run CRC calc on the data nibs
crcnib crc, usb16_poly_r
.end_crc
add pkt_cnt, #1
cmp pkt_cnt, pkt_tmp wcz
if_a mov retval, #ERR_PACKET ' Error if payload > expected size
if_a ret
' For full-speed at 80MHz, the time it takes to do the final byte write and
' CRC verify has likely put us into the EOP zone. The P2 smart pins keep the
' EOP flag "sticky" for 7-bits of J, but at 80MHz, it still could be possible
' to miss it, so cheat a bit and look for SOP clear here.
rqpin urx, dm
testb urx, #EOPB wc ' FIXME: checking for EOP set should work when > 80MHz
if_nc jmp #.wait_byte ' Next read will catch EOP at low-speed
' CRC OK = Payload CRC calc ^ packet's CRC bytes = $B001 (the USB-16 expected residual)
.chk_crc
sub pkt_cnt, #2 ' Adjust payload count to exclude the CRC bytes read
xor crc, ##USB16_RESIDUAL wz ' CRC of (data + transmitted CRC) XOR residual should equal zero
if_nz jmp #urx_packet ' CRC fail; discard data and wait until data re-sent or transfer timeout
mov retval, #PID_ACK
mov hctwait, ip_delay
call #poll_waitx
' Fall through to utx_handshake
'------------------------------------------------------------------------------
' Transmit a handshake PID. The routine assumes that the bus is IDLE and
' the appropriate IP delay has been inserted.
'------------------------------------------------------------------------------
' On entry:
' retval - handshake PID to send.
' On exit:
' retval unchanged.
'------------------------------------------------------------------------------
utx_handshake
testb hstatus, #DWNSTRM_HUBB wc
if_c call #utx_pre
mov utx, #OUT_SOP
call #utx_byte ' Send sync
mov utx, retval
call #utx_byte ' Send handshake PID
.idle
rqpin urx, dm
testb urx, #J_IDLEB wc
if_nc jmp #.idle ' Wait for IDLE to ensure the PID tx is complete
mov hctwait, tat_wait ' Ensure one turn-around time before next transaction
jmp #poll_waitx
'------------------------------------------------------------------------------
' Wait for the USB tx buffer to empty and feed it a new byte.
'------------------------------------------------------------------------------
' On entry:
' utx - byte to transmit.
' On exit:
'------------------------------------------------------------------------------
utx_byte
testp dp wc
if_nc jmp #utx_byte
akpin dp
waitx utx_tweak ' Wait #0 '#3 if < 180MHz, wait #3 '#20 if 180MHz+
_ret_ wypin utx, dm
'------------------------------------------------------------------------------
' Fetch the next data byte of a packet. Always check receiver status for EOP.
'------------------------------------------------------------------------------
' On entry:
' On exit:
' retval - the byte read.
' urx - the receiver status. The caller must check the hstatus reg EOP flag
' on return. If EOP is set, the byte in reg retval remains as the last byte
' received.
'------------------------------------------------------------------------------
urx_next
rdpin urx, dm
mov utx, #BYTE_TGLF ' Reg utx free in this context
and utx, urx
cmp newb_flg, utx wz ' Fetch a byte whenever the flags differ
if_nz xor newb_flg, #BYTE_TGLF ' Synchronize flags
if_nz getbyte retval, urx, #1 ' Fetch the new byte
if_nz ret ' New byte is priority, so return now
testb urx, #SOPB wc
testb urx, #BUS_ERRB wz
if_c_and_nz jmp #urx_next ' If SOP still raised and !BUS_ERRB a new byte should be coming
if_nc bith hstatus, #EOPB ' If EOP make it sticky, otherwise it's a bus error
ret
'------------------------------------------------------------------------------
' Calculate USB-5 CRC. The upper word of the CRC pre-calc table in LUT contains
' the data used for the USB-5 CRC lookups. The token packet is three bytes in
' length, and the PID is not included in the CRC calculation:
' CRC5 FRAME_NUMBER SOF (full-speed)
' CRC5 ENDP ADDRESS PID
' %00000_1111_1111111_xxxxxxxx
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - stuffed with the function endpoint, address and
' SETUP/IN/OUT/SOF PID according to the USB standard.
' On exit:
' ep_addr_pid - CRC value appended to the packet.
'------------------------------------------------------------------------------
calc_crc5
and ep_addr_pid, ##EP_ADDR_MASK ' Clear existing CRC, if any
mov htmp, ep_addr_pid
shr htmp, #8 ' PID not included in CRC calc
mov crc, #$1f ' Initial CRC5 value
rev htmp ' Input data reflected
setq htmp ' CRCNIB setup for data bits 0..7
crcnib crc, #USB5_POLY
crcnib crc, #USB5_POLY ' Data bits 0..7 calculated
shl htmp, #9 wc ' Shift out processed bits + 1 to set up CRC of remaining bits 8..10
crcbit crc, #USB5_POLY ' Inline instead of REP as we're in hubexec
shl htmp, #1 wc
crcbit crc, #USB5_POLY
shl htmp, #1 wc
crcbit crc, #USB5_POLY
xor crc, #$1f ' Final XOR value
shl crc, #8 + 11 ' CRC to bits 23..19 of the token packet
_ret_ or ep_addr_pid, crc ' Put the CRC in its new home
'------------------------------------------------------------------------------
' Full-speed/low-speed frame timing interrupt service routine.
'------------------------------------------------------------------------------
isr1_fsframe
getct iframe_ct_base
mov iframe_ct_new, iframe_ct_base
addct1 iframe_ct_new, _frame1ms_clks_
wxpin _usb_h_fs_nco_, dm ' Restore host mode and 12Mbs baud
.wait
testp dp wc
if_nc jmp #.wait
akpin dp
mov utx, #PID_SOF
wypin #OUT_SOP, dm ' Put start-of-packet SYNC field on the USB
call #utx_byte ' Send token PID byte
mov icrc, #$1f ' Prime the CRC5 pump
mov sof_pkt, frame ' CRC5 calculation done on the 11-bit frame number value
rev sof_pkt ' Input data reflected
setq sof_pkt ' CRCNIB setup for data bits 0..7
crcnib icrc, #USB5_POLY
crcnib icrc, #USB5_POLY ' Data bits 0..7 calculated
getbyte utx, frame, #0 ' Send the low byte of the frame number
call #utx_byte
shl sof_pkt, #8 ' Shift out processed bits to set up CRCBIT * 3
rep #2, #3 ' Three data bits left to process
shl sof_pkt, #1 wc
crcbit icrc, #USB5_POLY ' Data bits 8..10 calculated
xor icrc, #$1f ' Final XOR value
getbyte utx, frame, #1 ' Send remaining frame number bits
shl icrc, #3 ' Merge CRC to bits 7..3 of the final token byte
or utx, icrc
call #utx_byte ' Last start-of-frame byte is on the wire
mov isrtmp1, _ip_delay_fs_ ' Use normal inter-packet delay when full-speed
jmp #isr1_wait
isr1_lsframe
getct iframe_ct_base
mov iframe_ct_new, iframe_ct_base
addct1 iframe_ct_new, _frame1ms_clks_
.wait
testp dp wc
if_nc jmp #.wait
akpin dp
wypin #OUT_EOP, dm ' EOP is the low-speed keep-alive strobe
mov isrtmp1, _ip_delay_ls_ ' Normal inter-packet delay works when low-speed
isr1_wait
rqpin utx, dm
testb utx, #SOPB wc
if_c jmp #isr1_wait
add frame, #1 ' Next frame# and check for wrap around
and frame, ##$7ff
waitx isrtmp1 ' Make sure bus is idle
reti1
'------------------------------------------------------------------------------
' Wait for a window within the 1ms frame boundary that will ensure that a
' transaction will complete before the next frame is triggered.
'------------------------------------------------------------------------------
' On entry:
' On exit:
'------------------------------------------------------------------------------
wait_txn_ok
getct htmp2
sub htmp2, iframe_ct_base
testb hstatus, #LOW_SPEEDB wc
if_c cmp htmp2, _txn_ok_ls_ wcz
if_nc cmp htmp2, _txn_ok_fs_ wcz
if_a jmp #wait_txn_ok ' Not enough time, so wait until next frame
ret
'------------------------------------------------------------------------------
' A device connection was detected, or a bus reset was requested by the USB
' client. Set the appropriate smart pin FS/LS speed mode to match the device
' and perform a reset sequence prior to device enumeration.
'------------------------------------------------------------------------------
dev_reset
rqpin urx, dm
testb urx, #K_RESUMEB wc ' K differential "1" in FS mode signals low-speed
if_c call #set_speed_low ' The speed config subroutines must restore the caller C flag
if_nc call #set_speed_full ' state on return if it writes the C flag.
reset
setint1 #0 ' Don't want frame interrupt while in reset
wypin #OUT_SE0, dm ' Assert bus reset
waitx _reset_hold_ ' Spec is >= 10ms
wypin #OUT_IDLE, dm
mov frame, #0 ' Reset the frame timespan count
getct iframe_ct_base
mov iframe_ct_new, iframe_ct_base
addct1 iframe_ct_new, _frame1ms_clks_
mov htmp, frame ' Allow reset recovery time (Section 9.2.6.2)
add htmp, #36
setint1 #1 ' Set ISR event trigger to CT-passed-CT1
.framewait
cmp frame, htmp wcz
if_b jmp #.framewait
ret
'------------------------------------------------------------------------------
' Bulk hub<->hub byte copy. Does not check for src/dest buffer overlap.
'------------------------------------------------------------------------------
' On entry:
' PTRA - source address.
' PB - destination address.
' hr0 - length of copy, in bytes.
' On exit:
'------------------------------------------------------------------------------
hmemcpy
rdbyte htmp, ptra++
wrbyte htmp, pb
add pb, #1
_ret_ djnz hr0, #hmemcpy
'------------------------------------------------------------------------------
' A fatal USB error has occured. Notify the client and spin in a pseudo-idle
' loop until the errant device is disconnected.
'------------------------------------------------------------------------------
' On entry:
' On exit:
'------------------------------------------------------------------------------
host_error
if ERROR_LED >= 0
drvl host_error_led
end
jmp #host_reset ' See if it works...
'------------------------------------------------------------------------------
' Get HID descriptor buffer for current port
'------------------------------------------------------------------------------
' On entry:
' On exit:
' PB - HID descriptor buffer pointer
'------------------------------------------------------------------------------
get_hid_descr_buffer
mov pb, hdev_port
cmpsub pb, #1
mul pb, #HID_DESCR_LEN
_ret_ add pb, hid_descr_p
usb16_poly_r long USB16_POLY
' USB commands and error codes
cmd_data long 0
usb_err_code long ERR_NONE
' Parameters block assigned at cog startup
dm long USB_BASE_PIN + USB_DMINUS_OFFSET ' Client defines the basepin for four consecutive USB port pins
dp long USB_BASE_PIN + USB_DMINUS_OFFSET + 1
usb_enable_pin long USB_ENABLE_OFFSET < 0 ? -1 : USB_BASE_PIN + USB_ENABLE_OFFSET
if ACTIVITY_LED >= 0
host_active_led long ACTIVITY_LED ' Client defines the LED pin# for host bus activity
end
if ERROR_LED >= 0
host_error_led long ERROR_LED ' Client defines the LED pin# to light on error
end
hid_descr_p long 0
'hid_report_p long 0, 0[MAX_DEVICES]
'hkbd_report_p long 0
hub_descr_p long 0
urx_buff_p long 0
dev_desc_buff_p long 0
con_desc_buff_p long 0
cache_start_p long 0
cache_end_p long 0
if EMUPAD_MAX_PLAYER > 0
emupad_out_ptr long 0
end
save_sysclk long 0 ' Save the current sysclock as the client may change it
hdev_init_start
hdev_port res 1
hdev_next_datax res ((1+MAX_DEVICES)+3)/4 ' 1+7 byte
kbm_next_datax res 1
hub_intf_num res 1
hub_next_datax res 1
hub_status_chg res 1
hub_port_status res 1
if HAVE_MOUSE
ms_intf_num res 1
ms_in_max_pkt res 1
end
kb_intf_num res 1
kb_interval res 1
kb_in_max_pkt res 1
kb_max_index res 1
kb_led_states res 1
gp_intf_num res 1
gp_interval res 1
gp_max_index res 1
gp_descr_len res 1+MAX_DEVICES
hdev_init_end
' Initialized at cog startup:
hcog_base_addr res 1 ' This object's start address in hub, read from PTRB at cog creation
iframe_ct_new res 1
iframe_ct_base res 1
utx_tweak res 1 ' Sysclock speeds above ~120MHz need some fairy dust for USB tx
' This register block is reset to zero when a USB device connects
hreg_init_start
hstatus res 1 ' Host status flags
hctwait res 1 ' Poll-based wait clocks
ip_delay res 1 ' Inter-packet delay in bit periods for connected device speed
tat_wait res 1 ' Maximum bus turn-around time in bit periods for connected device speed
nak_retry res 1 ' NAK retry count, unlimited retries if zero
xfer_retry res 1 ' Control transfer retry count
retry res 1 ' Transaction retry count
utx res 1 ' Byte to transmit on USB
urx res 1 ' LSByte receiver status flags, MSByte received data
newb_flg res 1 ' Receive "new byte" bit toggle detector
max_pkt_size res 1 ' Maximum payload bytes allowed, likely to change on device connect.
total_data res 1 ' Total bytes to tx/rx in a transfer data stage
stage_data res 1 ' Count of bytes sent/received so far during a data stage.
pkt_data res 1 ' Payload size of an OUT packet or bytes received on IN
frame res 1 ' USB 1ms frame counter value
sof_pkt res 1 ' ISR frame# packet and CRC5
icrc res 1 ' Only used by the 1ms frame output ISR routine
pkt_cnt res 1 ' Count of DATAx packet payload bytes
crc res 1 ' Used for CRC16 calculation
ep_addr_pid res 1 ' Endpoint and device addresses for connected device
retval res 1 ' Global success/fail return parameter
context_retval res 1 ' Operation contextual return parameter
' Device stuff
hdev_id res 1+MAX_DEVICES
hdev_bcd res 1
hdev_type res ((1+MAX_DEVICES)+3)/4 ' Configured device indicator
' Keyboard/mouse stuff
hctrl_ep_addr res 1
hctrl_max_pkt res 1
hconfig_base res 1
hcon_tot_len res 1 ' Size of the complete config descriptor chain
hdev_intf_idx res 1 ' Used during verbose descriptor terminal output
hdev_class res 1
hdev_subclass res 1
hdev_protocol res 1
hsearch_key res 1 ' Descriptor type to search for in the config chain
hnext_desc res 1 ' Offset from the config descriptor start address to the next descriptor in the chain
hhub_ctrl_ep res 1 ' Hub control endpoint address
hhub_ep_addr res 1 ' Hub interface endpoint address
hkbd_ctrl_ep res 1 ' Keyboard control endpoint address
hkbd_ep_addr res 1 ' Keyboard interface endpoint address
if HAVE_MOUSE
hmouse_ep_addr res 1 ' Mouse interface endpoint address
end
hkbd_poll_cnt res 1 ' Poll interval counter used for key auto-repeat
hkbd_ledstates res 1 ' Off/on state of keyboard LEDs
hdev_ep_addr res 1+MAX_DEVICES
hdev_out_addr res 1
if EMUPAD_MAX_PLAYER > 0
hkbd_emupad_buffer res EMUPAD_MAX_PLAYER
hpad_emupad_buffer res EMUPAD_MAX_PLAYER
hpad_emupad_rule res 1+MAX_DEVICES ' This is PER REAL DEVICE
end
hreg_init_end
hidr_start
hidr_usage res 1 ' <- can get trashed after parsing
hidr_flags res 1
hidr_axis res 4
hidr_buttons res 1
hidr_hats res 1
'' Above are actually useful values, 6 longs
hidr_type res 1
hidr_id res 1
hidr_offset res 1
hidr_usage_idx res 1
' below need to be push/pop'd
hidr_state res 1
hidr_size res 1
hidr_count res 1
hidr_lminmax res 1
hidr_pminmax res 1
hidr_end
' Variables dependent on the system freqency
_var_64_lower_ res 1
_var_64_upper_ res 1
_12Mbps_ res 1
_1_5Mbps_ res 1
_1ns16fp_ res 1 ' 1ns as 32,16 fixed point
_1us_ res 1 ' 1us
_10us_ res 1 ' 10us
_33us_ res 1 ' 33us
_txn_err_ res 1 ' 250us
_500us_ res 1 ' 500us
_txn_ok_ls_ res 1 ' 666us timespan for LS transaction OK window
_txn_ok_fs_ res 1 ' 850us timespan for FS transaction OK window
_ip_delay_ls_ res 1 ' Low-Speed inter-packet 4 bit-time delay
_ip_delay_fs_ res 1 ' Full-Speed inter-packet 4 bit-time delay
_tat_wait_ls_ res 1 ' Low-Speed turnaround 22 bit-time wait
_tat_wait_fs_ res 1 ' Full-Speed turnaround 28 bit-time wait
_preamble_wait_ res 1
_1ms_ res 1 ' 1ms
_2ms_ res 1 ' 2ms
_suspend_wait_ res 1 ' 3ms
_4ms_ res 1 ' 4ms
_xfer_wait_ res 1 ' 5ms
_8ms_ res 1 ' 8ms timespan for keyboard/mouse interrupt IN transactions
_reset_hold_ res 1 ' 15ms
_resume_hold_ res 1 ' Hold K-state for 20ms to signal device(s) to resume
_21ms_ res 1 ' 21ms
_100ms_ res 1 ' 100ms
_500ms_ res 1 ' 500ms
_pulse_time_ res 1 ' Activity LED toggle interval, one sec connect wait, _500ms_ when connected
_frame1ms_clks_ res 1 '_1ms +/- n clocks: calculated based on the current sysclock
'------------------------------------------------------------------------------
_usb_h_ls_nco_ res 1 ' USB smart pin modes dependent on sysclock
_usb_d_ls_nco_ res 1
_usb_h_fs_nco_ res 1
_usb_d_fs_nco_ res 1
'------------------------------------------------------------------------------
' Scratch registers
htmp res 1 ' Scratch registers whose context remains within the same code block
htmp1 res 1
htmp2 res 1
htmp3 res 1
hrep res 1 ' Repeat count
hsave0 res 1 ' Subroutine parameter saves
hsave1 res 1
hsave2 res 1
isrtmp1 res 1
pkt_tmp res 1 ' Tmp storage for routines that deal with datax packets
hr0 res 1 ' Multi-purpose registers
hr1 res 1
hr2 res 1
hr3 res 1
hpar1 res 1 ' Routine entry/exit parameters
hpar2 res 1
hpar3 res 1
hct2 res 1 ' Function response bus turn-around timer
hct3 res 1 ' Keyboard/mouse poll timer
mod_cnt res 1 ' Used in idle loops
fit $1F0