3 #define HIGH(x) (((x) & 0xff00) / 256)
4 #define LOW(x) ((x) & 0xff)
10 ;;; our bit assignments
12 #define DO_TX_UNTHROTTLE 1
14 ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
23 #define EP0STALLbit #0x01
24 #define IN0BUF #0x7f00
26 #define OUT0BUF #0x7ec0
27 #define OUT0BC #0x7fc5
28 #define IN2BUF #0x7e00
31 #define OUT2BC #0x7fc9
32 #define OUT2CS #0x7fc8
33 #define OUT2BUF #0x7dc0
34 #define IN4BUF #0x7d00
42 #define PORTBCFG #0x7f94
43 #define PORTCCFG #0x7f95
45 #define IN07IRQ #0x7fa9
46 #define OUT07IRQ #0x7faa
47 #define IN07IEN #0x7fac
48 #define OUT07IEN #0x7fad
49 #define USBIRQ #0x7fab
50 #define USBIEN #0x7fae
51 #define USBBAV #0x7faf
53 #define SUDPTRH #0x7fd4
54 #define SUDPTRL #0x7fd5
55 #define SETUPDAT #0x7fe8
57 ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
68 .byte 0 ; filled in by the USB core
70 ;;; local variables. These are not initialized properly: do it by hand.
76 tx_unthrottle_threshold: .byte 0
78 .org 0x100H ; wants to be on a page boundary
80 ljmp ISR_Sudav ; Setup Data Available
82 ljmp 0 ; Start of Frame
84 ljmp 0 ; Setup Data Loading
86 ljmp 0 ; Global Suspend
92 ljmp 0 ; End Point 0 In
94 ljmp 0 ; End Point 0 Out
96 ljmp 0 ; End Point 1 In
98 ljmp 0 ; End Point 1 Out
108 start: mov SP,STACK-1 ; set stack
109 ;; clear local variables
115 mov tx_unthrottle_threshold, a
119 ;; clear fifo with "fe"
126 djnz r1, clear_tx_ring_loop
133 djnz r1, clear_rx_ring_loop
135 ;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
136 ;;; on Xircom the STANDBY is wired to PB6 and PC4
170 ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
175 ;; set up interrupts, autovectoring
179 setb acc.0 ; AVEN bit to 0
182 mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
184 movx @dptr, a ; clear SUDAVI
189 mov a,#0x04 ; enable IN2 int
193 mov a,#0x04 ; enable OUT2 int
196 movx @dptr, a ; arm OUT2
198 ;; mov a, #0x84 ; turn on RTS, DTR
202 mov a, #0x7 ; turn on DTR
206 mov a, #0x20 ; turn on the RED led
210 mov a, #0x80 ; turn on RTS
214 ;; setup the serial port. 9600 8N1.
215 mov a,#0x53 ; mode 1, enable rx, clear int
217 ;; using timer2, in 16-bit baud-rate-generator mode
218 ;; (xtal 12MHz, internal fosc 24MHz)
219 ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
220 ;; 57600: 0xFFF2.F, say 0xFFF3
221 ;; 9600: 0xFFB1.E, say 0xFFB2
224 #define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
225 #define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
226 #define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
228 mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
240 ;; hey, what say we RENUMERATE! (TRM p.62)
244 mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
246 ;; now presence pin is floating, simulating disconnect. wait 0.5s
255 djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
256 mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
258 ;; we are back online. the host device will now re-query us
274 mov EXIF,a ; clear INT2 first
275 mov dptr, USBIRQ ; clear USB int
282 mov r1, a ; r1 = bmRequestType
285 mov r2, a ; r2 = bRequest
288 mov r3, a ; r3 = wValueL
291 mov r4, a ; r4 = wValueH
293 ;; main switch on bmRequest.type: standard or vendor
296 cjne a, #0x00, setup_bmreq_type_not_standard
297 ;; standard request: now main switch is on bRequest
298 ljmp setup_bmreq_is_standard
300 setup_bmreq_type_not_standard:
301 ;; a still has bmreq&0x60
302 cjne a, #0x40, setup_bmreq_type_not_vendor
303 ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
304 ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
305 cjne r2, #0x00, setup_ctrl_not_00
306 ;; 00 is set baud, wValue[0] has baud rate index
307 lcall set_baud ; index in r3, carry set if error
308 jc setup_bmreq_type_not_standard__do_stall
310 setup_bmreq_type_not_standard__do_stall:
313 cjne r2, #0x01, setup_ctrl_not_01
314 ;; 01 is reserved for set bits (parity). TODO
317 cjne r2, #0x02, setup_ctrl_not_02
318 ;; 02 is set HW flow control. TODO
321 cjne r2, #0x03, setup_ctrl_not_03
322 ;; 03 is control pins (RTS, DTR).
323 ljmp control_pins ; will jump to setup_done_ack,
324 ; or setup_return_one_byte
326 cjne r2, #0x04, setup_ctrl_not_04
327 ;; 04 is send break (really "turn break on/off"). TODO
328 cjne r3, #0x00, setup_ctrl_do_break_on
329 ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
335 setup_ctrl_do_break_on:
336 ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
347 cjne r2, #0x05, setup_ctrl_not_05
348 ;; 05 is set desired interrupt bitmap. TODO
351 cjne r2, #0x06, setup_ctrl_not_06
353 cjne r3, #0x00, setup_ctrl_06_not_00
354 ;; 06, wValue[0]=0 is query write_room
357 subb a, tx_ring_in ; out-1-in = 255 - (in-out)
358 ljmp setup_return_one_byte
359 setup_ctrl_06_not_00:
360 cjne r3, #0x01, setup_ctrl_06_not_01
361 ;; 06, wValue[0]=1 is query chars_in_buffer
364 subb a, tx_ring_out ; in-out
365 ljmp setup_return_one_byte
366 setup_ctrl_06_not_01:
369 cjne r2, #0x07, setup_ctrl_not_07
370 ;; 07 is request tx unthrottle interrupt
371 mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
376 setup_bmreq_type_not_vendor:
380 setup_bmreq_is_standard:
381 cjne r2, #0x00, setup_breq_not_00
382 ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
383 cjne r1, #0x80, setup_Get_Status_not_device
384 ;; Get_Status(device)
385 ;; are we self-powered? no. can we do remote wakeup? no
386 ;; so return two zero bytes. This is reusable
387 setup_return_two_zero_bytes:
397 setup_Get_Status_not_device:
398 cjne r1, #0x82, setup_Get_Status_not_endpoint
399 ;; Get_Status(endpoint)
400 ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
401 ;; for now: cheat. TODO
402 sjmp setup_return_two_zero_bytes
403 setup_Get_Status_not_endpoint:
404 cjne r1, #0x81, setup_Get_Status_not_interface
405 ;; Get_Status(interface): return two zeros
406 sjmp setup_return_two_zero_bytes
407 setup_Get_Status_not_interface:
411 cjne r2, #0x01, setup_breq_not_01
412 ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
413 cjne r3, #0x00, setup_Clear_Feature_not_stall
414 ;; Clear_Feature(stall). should clear a stall bit. TODO
416 setup_Clear_Feature_not_stall:
417 cjne r3, #0x01, setup_Clear_Feature_not_rwake
418 ;; Clear_Feature(remote wakeup). ignored.
420 setup_Clear_Feature_not_rwake:
424 cjne r2, #0x03, setup_breq_not_03
425 ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
426 cjne r3, #0x00, setup_Set_Feature_not_stall
427 ;; Set_Feature(stall). Should set a stall bit. TODO
429 setup_Set_Feature_not_stall:
430 cjne r3, #0x01, setup_Set_Feature_not_rwake
431 ;; Set_Feature(remote wakeup). ignored.
433 setup_Set_Feature_not_rwake:
437 cjne r2, #0x06, setup_breq_not_06
438 ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
439 cjne r4, #0x01, setup_Get_Descriptor_not_device
440 ;; Get_Descriptor(device)
442 mov a, #HIGH(desc_device)
445 mov a, #LOW(desc_device)
448 setup_Get_Descriptor_not_device:
449 cjne r4, #0x02, setup_Get_Descriptor_not_config
450 ;; Get_Descriptor(config[n])
451 cjne r3, #0x00, setup_stall; only handle n==0
452 ;; Get_Descriptor(config[0])
454 mov a, #HIGH(desc_config1)
457 mov a, #LOW(desc_config1)
460 setup_Get_Descriptor_not_config:
461 cjne r4, #0x03, setup_Get_Descriptor_not_string
462 ;; Get_Descriptor(string[wValueL])
463 ;; if (wValueL >= maxstrings) stall
464 mov a, #((desc_strings_end-desc_strings)/2)
466 subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
470 add a, r3 ; a = 2*wValueL
471 mov dptr, #desc_strings
476 mov dph, a ; dph = desc_strings[a]. big endian! (handy)
477 ;; it looks like my adapter uses a revision of the EZUSB that
478 ;; contains "rev D errata number 8", as hinted in the EzUSB example
479 ;; code. I cannot find an actual errata description on the Cypress
480 ;; web site, but from the example code it looks like this bug causes
481 ;; the length of string descriptors to be read incorrectly, possibly
482 ;; sending back more characters than the descriptor has. The workaround
483 ;; is to manually send out all of the data. The consequence of not
484 ;; using the workaround is that the strings gathered by the kernel
485 ;; driver are too long and are filled with trailing garbage (including
486 ;; leftover strings). Writing this out by hand is a nuisance, so for
487 ;; now I will just live with the bug.
502 setup_Get_Descriptor_not_string:
506 cjne r2, #0x08, setup_breq_not_08
507 ;; Get_Configuration. always 1. return one byte.
510 setup_return_one_byte:
518 cjne r2, #0x09, setup_breq_not_09
519 ;; 09: Set_Configuration. ignored.
522 cjne r2, #0x0a, setup_breq_not_0a
523 ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
524 ;; since we only have one interface, ignore wIndexL, return a 0
526 ljmp setup_return_one_byte
528 cjne r2, #0x0b, setup_breq_not_0b
529 ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
559 ;;; ==============================================================
561 set_baud: ; baud index in r3
564 jb ACC.7, set_baud__badbaud
567 jnc set_baud__badbaud
570 add a, #LOW(baud_table)
572 mov a, #HIGH(baud_table)
575 ;; TODO: shut down xmit/receive
576 ;; TODO: wait for current xmit char to leave
577 ;; TODO: shut down timer to avoid partial-char glitch
578 movx a,@dptr ; BAUD_HIGH
582 movx a,@dptr ; BAUD_LOW
585 ;; TODO: restart xmit/receive
586 ;; TODO: reenable interrupts, resume tx if pending
590 setb c ; c=1: failure
593 ;;; ==================================================
595 cjne r1, #0x41, control_pins_in
598 mov a, r3 ; wValue[0] holds new bits: b7 is new RTS
599 xrl a, #0xff ; 1 means active, 0V, +12V ?
603 movx a, @dptr ; only change bit 7
606 movx @dptr, a ; other pins are inputs, bits ignored
612 ljmp setup_return_one_byte
614 ;;; ========================================
625 mov EXIF,a ; clear INT2 first
626 mov dptr, IN07IRQ ; clear USB int
630 mov a, #0x20 ; Turn off the green LED
638 mov a, #0x20 ; Turn off the green LED
660 mov a, #0x10 ; Turn the green LED
668 mov EXIF,a ; clear INT2 first
669 mov dptr, OUT07IRQ ; clear USB int
675 ;; copy data into buffer. for now, assume we will have enough space
676 mov dptr, OUT2BC ; get byte count
681 mov dptr, OUT2BUF ; load DPTR0 with source
682 mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
686 inc dps ; switch to DPTR1: target
687 inc dpl1 ; target = tx_ring_in+1
690 cjne a, tx_ring_out, OUT_no_overflow
693 inc tx_ring_in ; tx_ring_in++
694 inc dps ; switch to DPTR0: source
710 mov a, #0x20 ; Turn off the green LED
723 ;; fill in EP4in with a debugging message:
724 ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
734 jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
752 jnb TX_RUNNING, dump_stat__no_tx_running
754 dump_stat__no_tx_running:
759 mov dptr, #tx_ring ; DPTR1: source
761 dump_stat__tx_ring_loop:
768 djnz r1, dump_stat__tx_ring_loop
777 mov dptr, #rx_ring ; DPTR1: source
779 dump_stat__rx_ring_loop:
786 djnz r1, dump_stat__rx_ring_loop
797 ;;; ============================================================
800 ;; make sure the tx process is running.
801 jb TX_RUNNING, start_tx_done
803 ;; is there work to be done?
805 cjne a,tx_ring_out, start_tx__work
808 ;; tx was not running. send the first character, setup the TI int
809 inc tx_ring_out ; [++tx_ring_out]
810 mov dph, #HIGH(tx_ring)
816 ;; can we unthrottle the host tx process?
817 ;; step 1: do we care?
819 cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
821 start_tx_really_done:
823 start_tx__maybe_unthrottle_tx:
824 ;; step 2: is there now room?
828 ;; a is now write_room. If thresh >= a, we can unthrottle
830 subb a, tx_unthrottle_threshold
831 jc start_tx_really_done ; nope
832 ;; yes, we can unthrottle. remove the threshold and mark a request
833 mov tx_unthrottle_threshold, #0
834 setb DO_TX_UNTHROTTLE
835 ;; prod rx, which will actually send the message when in2 becomes free
846 jnb TI, serial_int__not_tx
847 ;; tx finished. send another character if we have one
852 jnb RI, serial_int__not_rx
866 mov dph, #HIGH(rx_ring)
868 inc dpl ; target = rx_ring_in+1
871 ;; check for overflow before incrementing rx_ring_in
873 cjne a, rx_ring_out, get_rx_char__no_overflow
876 get_rx_char__no_overflow:
878 ;; kick off USB INpipe
883 ;; check if the inpipe is already running.
890 jb acc.1, start_in__done; int will handle it
891 jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
892 ;; see if there is any work to do. a serial interrupt might occur
893 ;; during this sequence?
895 cjne a, rx_ring_out, start_in__have_work
898 ;; now copy as much data as possible into the pipe. 63 bytes max.
901 mov dph, #HIGH(rx_ring) ; load DPTR0 with source
903 mov dptr, IN2BUF ; load DPTR1 with target
904 movx @dptr, a ; in[0] signals that rest of IN is rx data
907 ;; loop until we run out of data, or we have copied 64 bytes
908 mov r1, #1 ; INbuf size counter
911 cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
913 start_inlocal_irq_enablell_copying:
918 movx @dptr, a ; write into IN buffer
922 cjne r1, #64, start_in__loop; loop
924 ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
926 mov a, #0x10 ; Turn the green LED
937 start_in__do_tx_unthrottle:
938 ;; special sequence: send a tx unthrottle message
961 baud_table: ; baud_high, then baud_low
969 .byte BAUD_HIGH(1200)
972 .byte BAUD_HIGH(2400)
975 .byte BAUD_HIGH(4800)
978 .byte BAUD_HIGH(9600)
981 .byte BAUD_HIGH(19200)
982 .byte BAUD_LOW(19200)
984 .byte BAUD_HIGH(38400)
985 .byte BAUD_LOW(38400)
987 .byte BAUD_HIGH(57600)
988 .byte BAUD_LOW(57600)
990 .byte BAUD_HIGH(115200)
991 .byte BAUD_LOW(115200)
994 .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
995 .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
996 ;;; The "real" device id, which must match the host driver, is that
997 ;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1000 .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1001 .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1002 .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1003 .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1006 .word string_langids, string_mfg, string_product, string_serial
1009 string_langids: .byte string_langids_end-string_langids
1014 ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
1015 ;; *that* is a pain in the ass to encode. And they are little-endian
1016 ;; too. Use this perl snippet to get the bytecodes:
1020 printf("0x%02x, 0x00, ", ord($c));
1025 string_mfg: .byte string_mfg_end-string_mfg
1027 ; .byte "ACME usb widgets"
1028 .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
1031 string_product: .byte string_product_end-string_product
1033 ; .byte "ACME USB serial widget"
1034 .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
1037 string_serial: .byte string_serial_end-string_serial
1040 .byte 0x34, 0x00, 0x37, 0x00
1043 ;;; ring buffer memory
1044 ;; tx_ring_in+1 is where the next input byte will go
1045 ;; [tx_ring_out] has been sent
1046 ;; if tx_ring_in == tx_ring_out, theres no work to do
1047 ;; there are (tx_ring_in - tx_ring_out) chars to be written
1048 ;; dont let _in lap _out
1049 ;; cannot inc if tx_ring_in+1 == tx_ring_out
1050 ;; write [tx_ring_in+1] then tx_ring_in++
1051 ;; if (tx_ring_in+1 == tx_ring_out), overflow
1052 ;; else tx_ring_in++
1053 ;; read/send [tx_ring_out+1], then tx_ring_out++
1055 ;; rx_ring_in works the same way
1059 .skip 0x100 ; 256 bytes
1061 .skip 0x100 ; 256 bytes