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 PORTCCFG #0x7f95
43 #define IN07IRQ #0x7fa9
44 #define OUT07IRQ #0x7faa
45 #define IN07IEN #0x7fac
46 #define OUT07IEN #0x7fad
47 #define USBIRQ #0x7fab
48 #define USBIEN #0x7fae
49 #define USBBAV #0x7faf
51 #define SUDPTRH #0x7fd4
52 #define SUDPTRL #0x7fd5
53 #define SETUPDAT #0x7fe8
55 ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
66 .byte 0 ; filled in by the USB core
68 ;;; local variables. These are not initialized properly: do it by hand.
74 tx_unthrottle_threshold: .byte 0
76 .org 0x100H ; wants to be on a page boundary
78 ljmp ISR_Sudav ; Setup Data Available
80 ljmp 0 ; Start of Frame
82 ljmp 0 ; Setup Data Loading
84 ljmp 0 ; Global Suspend
90 ljmp 0 ; End Point 0 In
92 ljmp 0 ; End Point 0 Out
94 ljmp 0 ; End Point 1 In
96 ljmp 0 ; End Point 1 Out
106 start: mov SP,STACK-1 ; set stack
107 ;; clear local variables
113 mov tx_unthrottle_threshold, a
117 ;; clear fifo with "fe"
124 djnz r1, clear_tx_ring_loop
131 djnz r1, clear_rx_ring_loop
133 ;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
146 ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
151 ;; set up interrupts, autovectoring
154 setb acc.0 ; AVEN bit to 0
157 mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
159 movx @dptr, a ; clear SUDAVI
164 mov a,#0x04 ; enable IN2 int
168 mov a,#0x04 ; enable OUT2 int
171 movx @dptr, a ; arm OUT2
173 mov a, #0x84 ; turn on RTS, DTR
176 ;; setup the serial port. 9600 8N1.
177 mov a,#01010011 ; mode 1, enable rx, clear int
179 ;; using timer2, in 16-bit baud-rate-generator mode
180 ;; (xtal 12MHz, internal fosc 24MHz)
181 ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
182 ;; 57600: 0xFFF2.F, say 0xFFF3
183 ;; 9600: 0xFFB1.E, say 0xFFB2
186 #define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
187 #define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
188 #define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
190 mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
202 ;; hey, what say we RENUMERATE! (TRM p.62)
206 mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
208 ;; now presence pin is floating, simulating disconnect. wait 0.5s
217 djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
218 mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
220 ;; we are back online. the host device will now re-query us
236 mov EXIF,a ; clear INT2 first
237 mov dptr, USBIRQ ; clear USB int
244 mov r1, a ; r1 = bmRequestType
247 mov r2, a ; r2 = bRequest
250 mov r3, a ; r3 = wValueL
253 mov r4, a ; r4 = wValueH
255 ;; main switch on bmRequest.type: standard or vendor
258 cjne a, #0x00, setup_bmreq_type_not_standard
259 ;; standard request: now main switch is on bRequest
260 ljmp setup_bmreq_is_standard
262 setup_bmreq_type_not_standard:
263 ;; a still has bmreq&0x60
264 cjne a, #0x40, setup_bmreq_type_not_vendor
265 ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
266 ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
267 cjne r2, #0x00, setup_ctrl_not_00
268 ;; 00 is set baud, wValue[0] has baud rate index
269 lcall set_baud ; index in r3, carry set if error
270 jc setup_bmreq_type_not_standard__do_stall
272 setup_bmreq_type_not_standard__do_stall:
275 cjne r2, #0x01, setup_ctrl_not_01
276 ;; 01 is reserved for set bits (parity). TODO
279 cjne r2, #0x02, setup_ctrl_not_02
280 ;; 02 is set HW flow control. TODO
283 cjne r2, #0x03, setup_ctrl_not_03
284 ;; 03 is control pins (RTS, DTR).
285 ljmp control_pins ; will jump to setup_done_ack,
286 ; or setup_return_one_byte
288 cjne r2, #0x04, setup_ctrl_not_04
289 ;; 04 is send break (really "turn break on/off"). TODO
290 cjne r3, #0x00, setup_ctrl_do_break_on
291 ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
297 setup_ctrl_do_break_on:
298 ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
309 cjne r2, #0x05, setup_ctrl_not_05
310 ;; 05 is set desired interrupt bitmap. TODO
313 cjne r2, #0x06, setup_ctrl_not_06
315 cjne r3, #0x00, setup_ctrl_06_not_00
316 ;; 06, wValue[0]=0 is query write_room
319 subb a, tx_ring_in ; out-1-in = 255 - (in-out)
320 ljmp setup_return_one_byte
321 setup_ctrl_06_not_00:
322 cjne r3, #0x01, setup_ctrl_06_not_01
323 ;; 06, wValue[0]=1 is query chars_in_buffer
326 subb a, tx_ring_out ; in-out
327 ljmp setup_return_one_byte
328 setup_ctrl_06_not_01:
331 cjne r2, #0x07, setup_ctrl_not_07
332 ;; 07 is request tx unthrottle interrupt
333 mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
338 setup_bmreq_type_not_vendor:
342 setup_bmreq_is_standard:
343 cjne r2, #0x00, setup_breq_not_00
344 ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
345 cjne r1, #0x80, setup_Get_Status_not_device
346 ;; Get_Status(device)
347 ;; are we self-powered? no. can we do remote wakeup? no
348 ;; so return two zero bytes. This is reusable
349 setup_return_two_zero_bytes:
359 setup_Get_Status_not_device:
360 cjne r1, #0x82, setup_Get_Status_not_endpoint
361 ;; Get_Status(endpoint)
362 ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
363 ;; for now: cheat. TODO
364 sjmp setup_return_two_zero_bytes
365 setup_Get_Status_not_endpoint:
366 cjne r1, #0x81, setup_Get_Status_not_interface
367 ;; Get_Status(interface): return two zeros
368 sjmp setup_return_two_zero_bytes
369 setup_Get_Status_not_interface:
373 cjne r2, #0x01, setup_breq_not_01
374 ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
375 cjne r3, #0x00, setup_Clear_Feature_not_stall
376 ;; Clear_Feature(stall). should clear a stall bit. TODO
378 setup_Clear_Feature_not_stall:
379 cjne r3, #0x01, setup_Clear_Feature_not_rwake
380 ;; Clear_Feature(remote wakeup). ignored.
382 setup_Clear_Feature_not_rwake:
386 cjne r2, #0x03, setup_breq_not_03
387 ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
388 cjne r3, #0x00, setup_Set_Feature_not_stall
389 ;; Set_Feature(stall). Should set a stall bit. TODO
391 setup_Set_Feature_not_stall:
392 cjne r3, #0x01, setup_Set_Feature_not_rwake
393 ;; Set_Feature(remote wakeup). ignored.
395 setup_Set_Feature_not_rwake:
399 cjne r2, #0x06, setup_breq_not_06
400 ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
401 cjne r4, #0x01, setup_Get_Descriptor_not_device
402 ;; Get_Descriptor(device)
404 mov a, #HIGH(desc_device)
407 mov a, #LOW(desc_device)
410 setup_Get_Descriptor_not_device:
411 cjne r4, #0x02, setup_Get_Descriptor_not_config
412 ;; Get_Descriptor(config[n])
413 cjne r3, #0x00, setup_stall; only handle n==0
414 ;; Get_Descriptor(config[0])
416 mov a, #HIGH(desc_config1)
419 mov a, #LOW(desc_config1)
422 setup_Get_Descriptor_not_config:
423 cjne r4, #0x03, setup_Get_Descriptor_not_string
424 ;; Get_Descriptor(string[wValueL])
425 ;; if (wValueL >= maxstrings) stall
426 mov a, #((desc_strings_end-desc_strings)/2)
428 subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
432 add a, r3 ; a = 2*wValueL
433 mov dptr, #desc_strings
438 mov dph, a ; dph = desc_strings[a]. big endian! (handy)
439 ;; it looks like my adapter uses a revision of the EZUSB that
440 ;; contains "rev D errata number 8", as hinted in the EzUSB example
441 ;; code. I cannot find an actual errata description on the Cypress
442 ;; web site, but from the example code it looks like this bug causes
443 ;; the length of string descriptors to be read incorrectly, possibly
444 ;; sending back more characters than the descriptor has. The workaround
445 ;; is to manually send out all of the data. The consequence of not
446 ;; using the workaround is that the strings gathered by the kernel
447 ;; driver are too long and are filled with trailing garbage (including
448 ;; leftover strings). Writing this out by hand is a nuisance, so for
449 ;; now I will just live with the bug.
464 setup_Get_Descriptor_not_string:
468 cjne r2, #0x08, setup_breq_not_08
469 ;; Get_Configuration. always 1. return one byte.
472 setup_return_one_byte:
480 cjne r2, #0x09, setup_breq_not_09
481 ;; 09: Set_Configuration. ignored.
484 cjne r2, #0x0a, setup_breq_not_0a
485 ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
486 ;; since we only have one interface, ignore wIndexL, return a 0
488 ljmp setup_return_one_byte
490 cjne r2, #0x0b, setup_breq_not_0b
491 ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
521 ;;; ==============================================================
523 set_baud: ; baud index in r3
526 jb ACC.7, set_baud__badbaud
529 jnc set_baud__badbaud
532 add a, #LOW(baud_table)
534 mov a, #HIGH(baud_table)
537 ;; TODO: shut down xmit/receive
538 ;; TODO: wait for current xmit char to leave
539 ;; TODO: shut down timer to avoid partial-char glitch
540 movx a,@dptr ; BAUD_HIGH
544 movx a,@dptr ; BAUD_LOW
547 ;; TODO: restart xmit/receive
548 ;; TODO: reenable interrupts, resume tx if pending
552 setb c ; c=1: failure
555 ;;; ==================================================
557 cjne r1, #0x41, control_pins_in
559 mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS
560 xrl a, #0xff ; 1 means active, 0V, +12V ?
564 movx a, @dptr ; only change bits 7 and 2
567 movx @dptr, a ; other pins are inputs, bits ignored
573 ljmp setup_return_one_byte
575 ;;; ========================================
586 mov EXIF,a ; clear INT2 first
587 mov dptr, IN07IRQ ; clear USB int
611 mov EXIF,a ; clear INT2 first
612 mov dptr, OUT07IRQ ; clear USB int
618 ;; copy data into buffer. for now, assume we will have enough space
619 mov dptr, OUT2BC ; get byte count
624 mov dptr, OUT2BUF ; load DPTR0 with source
625 mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
629 inc dps ; switch to DPTR1: target
630 inc dpl1 ; target = tx_ring_in+1
633 cjne a, tx_ring_out, OUT_no_overflow
636 inc tx_ring_in ; tx_ring_in++
637 inc dps ; switch to DPTR0: source
662 ;; fill in EP4in with a debugging message:
663 ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
673 jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
691 jnb TX_RUNNING, dump_stat__no_tx_running
693 dump_stat__no_tx_running:
698 mov dptr, #tx_ring ; DPTR1: source
700 dump_stat__tx_ring_loop:
707 djnz r1, dump_stat__tx_ring_loop
716 mov dptr, #rx_ring ; DPTR1: source
718 dump_stat__rx_ring_loop:
725 djnz r1, dump_stat__rx_ring_loop
736 ;;; ============================================================
739 ;; make sure the tx process is running.
740 jb TX_RUNNING, start_tx_done
742 ;; is there work to be done?
744 cjne a,tx_ring_out, start_tx__work
747 ;; tx was not running. send the first character, setup the TI int
748 inc tx_ring_out ; [++tx_ring_out]
749 mov dph, #HIGH(tx_ring)
755 ;; can we unthrottle the host tx process?
756 ;; step 1: do we care?
758 cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
760 start_tx_really_done:
762 start_tx__maybe_unthrottle_tx:
763 ;; step 2: is there now room?
767 ;; a is now write_room. If thresh >= a, we can unthrottle
769 subb a, tx_unthrottle_threshold
770 jc start_tx_really_done ; nope
771 ;; yes, we can unthrottle. remove the threshold and mark a request
772 mov tx_unthrottle_threshold, #0
773 setb DO_TX_UNTHROTTLE
774 ;; prod rx, which will actually send the message when in2 becomes free
785 jnb TI, serial_int__not_tx
786 ;; tx finished. send another character if we have one
791 jnb RI, serial_int__not_rx
805 mov dph, #HIGH(rx_ring)
807 inc dpl ; target = rx_ring_in+1
810 ;; check for overflow before incrementing rx_ring_in
812 cjne a, rx_ring_out, get_rx_char__no_overflow
815 get_rx_char__no_overflow:
817 ;; kick off USB INpipe
822 ;; check if the inpipe is already running.
825 jb acc.1, start_in__done; int will handle it
826 jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
827 ;; see if there is any work to do. a serial interrupt might occur
828 ;; during this sequence?
830 cjne a, rx_ring_out, start_in__have_work
833 ;; now copy as much data as possible into the pipe. 63 bytes max.
836 mov dph, #HIGH(rx_ring) ; load DPTR0 with source
838 mov dptr, IN2BUF ; load DPTR1 with target
839 movx @dptr, a ; in[0] signals that rest of IN is rx data
842 ;; loop until we run out of data, or we have copied 64 bytes
843 mov r1, #1 ; INbuf size counter
846 cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
848 start_inlocal_irq_enablell_copying:
853 movx @dptr, a ; write into IN buffer
857 cjne r1, #64, start_in__loop; loop
859 ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
869 start_in__do_tx_unthrottle:
870 ;; special sequence: send a tx unthrottle message
893 baud_table: ; baud_high, then baud_low
901 .byte BAUD_HIGH(1200)
904 .byte BAUD_HIGH(2400)
907 .byte BAUD_HIGH(4800)
910 .byte BAUD_HIGH(9600)
913 .byte BAUD_HIGH(19200)
914 .byte BAUD_LOW(19200)
916 .byte BAUD_HIGH(38400)
917 .byte BAUD_LOW(38400)
919 .byte BAUD_HIGH(57600)
920 .byte BAUD_LOW(57600)
922 .byte BAUD_HIGH(115200)
923 .byte BAUD_LOW(115200)
926 .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
927 .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
928 ;;; The "real" device id, which must match the host driver, is that
929 ;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
932 .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
933 .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
934 .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
935 .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
938 .word string_langids, string_mfg, string_product, string_serial
941 string_langids: .byte string_langids_end-string_langids
946 ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
947 ;; *that* is a pain in the ass to encode. And they are little-endian
948 ;; too. Use this perl snippet to get the bytecodes:
952 printf("0x%02x, 0x00, ", ord($c));
957 string_mfg: .byte string_mfg_end-string_mfg
959 ; .byte "ACME usb widgets"
960 .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
963 string_product: .byte string_product_end-string_product
965 ; .byte "ACME USB serial widget"
966 .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
969 string_serial: .byte string_serial_end-string_serial
972 .byte 0x34, 0x00, 0x37, 0x00
975 ;;; ring buffer memory
976 ;; tx_ring_in+1 is where the next input byte will go
977 ;; [tx_ring_out] has been sent
978 ;; if tx_ring_in == tx_ring_out, theres no work to do
979 ;; there are (tx_ring_in - tx_ring_out) chars to be written
980 ;; dont let _in lap _out
981 ;; cannot inc if tx_ring_in+1 == tx_ring_out
982 ;; write [tx_ring_in+1] then tx_ring_in++
983 ;; if (tx_ring_in+1 == tx_ring_out), overflow
985 ;; read/send [tx_ring_out+1], then tx_ring_out++
987 ;; rx_ring_in works the same way
991 .skip 0x100 ; 256 bytes
993 .skip 0x100 ; 256 bytes