1 /*  $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
 
   3  *  Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
 
   4  *  the EzUSB microcontroller.
 
   6  *  (C) Copyright 2000 Brian Warner <warner@lothar.com>
 
   8  *      This program is free software; you can redistribute it and/or modify
 
   9  *      it under the terms of the GNU General Public License as published by
 
  10  *      the Free Software Foundation; either version 2 of the License, or
 
  11  *      (at your option) any later version.
 
  13  *  "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
 
  16  *  This serial adapter is basically an EzUSB chip and an RS-232 line driver
 
  17  *  in a little widget that has a DB-9 on one end and a USB plug on the other.
 
  18  *  It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
 
  19  *  as a baud-rate generator. The wiring is:
 
  20  *   PC0/RxD0 <- rxd (DB9 pin 2)         PC4 <- dsr pin 6
 
  21  *   PC1/TxD0 -> txd pin 3               PC5 <- ri  pin 9
 
  22  *   PC2      -> rts pin 7               PC6 <- dcd pin 1
 
  23  *   PC3      <- cts pin 8               PC7 -> dtr pin 4
 
  24  *   PB1 -> line driver standby
 
  26  *  The EzUSB register constants below come from their excellent documentation
 
  27  *  and sample code (which used to be available at www.anchorchips.com, but
 
  28  *  that has now been absorbed into Cypress' site and the CD-ROM contents
 
  29  *  don't appear to be available online anymore). If we get multiple
 
  30  *  EzUSB-based drivers into the kernel, it might be useful to pull them out
 
  31  *  into a separate .h file.
 
  33  * THEORY OF OPERATION:
 
  35  *   There are two 256-byte ring buffers, one for tx, one for rx.
 
  37  *   EP2out is pure tx data. When it appears, the data is copied into the tx
 
  38  *   ring and serial transmission is started if it wasn't already running. The
 
  39  *   "tx buffer empty" interrupt may kick off another character if the ring
 
  40  *   still has data. If the host is tx-blocked because the ring filled up,
 
  41  *   it will request a "tx unthrottle" interrupt. If sending a serial character
 
  42  *   empties the ring below the desired threshold, we set a bit that will send
 
  43  *   up the tx unthrottle message as soon as the rx buffer becomes free.
 
  45  *   EP2in (interrupt) is used to send both rx chars and rx status messages
 
  46  *   (only "tx unthrottle" at this time) back up to the host. The first byte
 
  47  *   of the rx message indicates data (0) or status msg (1). Status messages
 
  48  *   are sent before any data.
 
  50  *   Incoming serial characters are put into the rx ring by the serial
 
  51  *   interrupt, and the EP2in buffer sent if it wasn't already in transit.
 
  52  *   When the EP2in buffer returns, the interrupt prompts us to send more
 
  53  *   rx chars (or status messages) if they are pending.
 
  55  *   Device control happens through "vendor specific" control messages on EP0.
 
  56  *   All messages are destined for the "Interface" (with the index always 0,
 
  57  *   so that if their two-port device might someday use similar firmware, we
 
  58  *   can use index=1 to refer to the second port). The messages defined are:
 
  60  *    bRequest = 0 : set baud/bits/parity
 
  62  *               2 : reserved for setting HW flow control (CTSRTS)
 
  63  *               3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
 
  64  *               4 : set break (on/off)
 
  65  *               5 : reserved for requesting interrupts on pin state change
 
  66  *               6 : query buffer room or chars in tx buffer
 
  67  *               7 : request tx unthrottle interrupt
 
  69  *  The host-side driver is set to recognize the device ID values stashed in
 
  70  *  serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
 
  71  *  start it running. This firmware will use EzUSB's "renumeration" trick by
 
  72  *  simulating a bus disconnect, then reconnect with a different device ID
 
  73  *  (encoded in the desc_device descriptor below). The host driver then
 
  74  *  recognizes the new device ID and glues it to the real serial driver code.
 
  77  *  EzUSB Technical Reference Manual: <http://www.anchorchips.com>
 
  78  *  8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
 
  79  *   basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
 
  80  *   use totally different registers!
 
  81  *  USB 1.1 spec: www.usb.org
 
  84  *  gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
 
  85  *  as31 -l keyspan_pda.asm
 
  86  *  mv keyspan_pda.obj keyspan_pda.hex
 
  87  *  perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
 
  88  * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
 
  89  * a bit to make it build.
 
  92  *  Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
 
  93  *  AnchorChips, for making such an incredibly useful little microcontroller.
 
  94  *  KeySpan, for making a handy, cheap ($40) widget that was so easy to take
 
  95  *           apart and trace with an ohmmeter.
 
  98  *  lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
 
  99  *  control. Interrupting host upon change in DCD, etc, counting transitions.
 
 100  *  Need to find a safe device id to use (the one used by the Keyspan firmware
 
 101  *  under Windows would be ideal.. can anyone figure out what it is?). Parity.
 
 102  *  More baud rates. Oh, and the string-descriptor-length silicon bug
 
 103  *  workaround should be implemented, but I'm lazy, and the consequence is
 
 104  *  that the device name strings that show up in your kernel log will have
 
 105  *  lots of trailing binary garbage in them (appears as ????). Device strings
 
 106  *  should be made more accurate.
 
 108  * Questions, bugs, patches to Brian.
 
 110  *  -Brian Warner <warner@lothar.com>
 
 114 #define HIGH(x) (((x) & 0xff00) / 256)
 
 115 #define LOW(x) ((x) & 0xff)
 
 121 ;;; our bit assignments
 
 123 #define DO_TX_UNTHROTTLE 1
 
 125         ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
 
 126 #define STACK #0x60-1
 
 133 #define EP0CS #0x7fb4
 
 134 #define EP0STALLbit #0x01
 
 135 #define IN0BUF #0x7f00
 
 136 #define IN0BC #0x7fb5
 
 137 #define OUT0BUF #0x7ec0
 
 138 #define OUT0BC #0x7fc5          
 
 139 #define IN2BUF #0x7e00
 
 140 #define IN2BC #0x7fb9
 
 141 #define IN2CS #0x7fb8
 
 142 #define OUT2BC #0x7fc9
 
 143 #define OUT2CS #0x7fc8
 
 144 #define OUT2BUF #0x7dc0
 
 145 #define IN4BUF #0x7d00
 
 146 #define IN4BC #0x7fbd
 
 147 #define IN4CS #0x7fbc
 
 152 #define PINSC #0x7f9b
 
 153 #define PORTBCFG #0x7f94
 
 154 #define PORTCCFG #0x7f95
 
 156 #define IN07IRQ #0x7fa9
 
 157 #define OUT07IRQ #0x7faa
 
 158 #define IN07IEN #0x7fac
 
 159 #define OUT07IEN #0x7fad
 
 160 #define USBIRQ #0x7fab
 
 161 #define USBIEN #0x7fae
 
 162 #define USBBAV #0x7faf
 
 163 #define USBCS #0x7fd6
 
 164 #define SUDPTRH #0x7fd4
 
 165 #define SUDPTRL #0x7fd5
 
 166 #define SETUPDAT #0x7fe8
 
 168         ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
 
 179         .byte 0                 ; filled in by the USB core
 
 181 ;;; local variables. These are not initialized properly: do it by hand.
 
 187 tx_unthrottle_threshold:        .byte 0
 
 189         .org 0x100H             ; wants to be on a page boundary
 
 191         ljmp    ISR_Sudav       ; Setup Data Available
 
 193         ljmp    0               ; Start of Frame
 
 195         ljmp    0               ; Setup Data Loading
 
 197         ljmp    0               ; Global Suspend
 
 203         ljmp    0               ; End Point 0 In
 
 205         ljmp    0               ; End Point 0 Out
 
 207         ljmp    0               ; End Point 1 In
 
 209         ljmp    0               ; End Point 1 Out
 
 219 start:  mov SP,STACK-1 ; set stack
 
 220         ;; clear local variables
 
 226         mov tx_unthrottle_threshold, a
 
 230         ;; clear fifo with "fe"
 
 237         djnz r1, clear_tx_ring_loop
 
 244         djnz r1, clear_rx_ring_loop
 
 246 ;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
 
 247 ;;; on Xircom the STANDBY is wired to PB6 and PC4 
 
 281         ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
 
 286         ;; set up interrupts, autovectoring
 
 290         setb acc.0              ; AVEN bit to 0
 
 293         mov a,#0x01             ; enable SUDAV: setup data available (for ep0)
 
 295         movx @dptr, a           ; clear SUDAVI
 
 300         mov a,#0x04             ; enable IN2 int
 
 304         mov a,#0x04             ; enable OUT2 int
 
 307         movx @dptr, a           ; arm OUT2
 
 309 ;;      mov a, #0x84            ; turn on RTS, DTR
 
 313         mov a, #0x7             ; turn on  DTR
 
 317         mov a, #0x20             ; turn on the RED led 
 
 321         mov a, #0x80            ; turn on  RTS
 
 325         ;; setup the serial port. 9600 8N1.
 
 326         mov a,#0x53             ; mode 1, enable rx, clear int
 
 328         ;;  using timer2, in 16-bit baud-rate-generator mode
 
 329         ;;   (xtal 12MHz, internal fosc 24MHz)
 
 330         ;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
 
 331         ;;  57600: 0xFFF2.F, say 0xFFF3
 
 332         ;;   9600: 0xFFB1.E, say 0xFFB2
 
 335 #define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
 
 336 #define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
 
 337 #define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
 
 339         mov T2CON, #030h        ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
 
 367         ;; hey, what say we RENUMERATE! (TRM p.62)
 
 371         mov a, #0x02            ; DISCON=0, DISCOE=0, RENUM=1
 
 373         ;; now presence pin is floating, simulating disconnect. wait 0.5s
 
 382         djnz r1, renum_wait1    ; wait about n*(256^2) 6MHz clocks
 
 383         mov a, #0x06            ; DISCON=0, DISCOE=1, RENUM=1
 
 385         ;; we are back online. the host device will now re-query us
 
 401         mov EXIF,a              ; clear INT2 first
 
 402         mov dptr, USBIRQ        ; clear USB int
 
 409         mov r1, a               ; r1 = bmRequestType
 
 412         mov r2, a               ; r2 = bRequest
 
 415         mov r3, a               ; r3 = wValueL
 
 418         mov r4, a               ; r4 = wValueH
 
 420         ;; main switch on bmRequest.type: standard or vendor
 
 423         cjne a, #0x00, setup_bmreq_type_not_standard
 
 424         ;; standard request: now main switch is on bRequest
 
 425         ljmp setup_bmreq_is_standard
 
 427 setup_bmreq_type_not_standard:  
 
 428         ;; a still has bmreq&0x60
 
 429         cjne a, #0x40, setup_bmreq_type_not_vendor
 
 430         ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
 
 431         ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
 
 432         cjne r2, #0x00, setup_ctrl_not_00
 
 433         ;; 00 is set baud, wValue[0] has baud rate index
 
 434         lcall set_baud          ; index in r3, carry set if error
 
 435         jc setup_bmreq_type_not_standard__do_stall
 
 437 setup_bmreq_type_not_standard__do_stall:
 
 440         cjne r2, #0x01, setup_ctrl_not_01
 
 441         ;; 01 is reserved for set bits (parity). TODO
 
 444         cjne r2, #0x02, setup_ctrl_not_02
 
 445         ;; 02 is set HW flow control. TODO
 
 448         cjne r2, #0x03, setup_ctrl_not_03
 
 449         ;; 03 is control pins (RTS, DTR).
 
 450         ljmp control_pins       ; will jump to setup_done_ack,
 
 451                                 ;  or setup_return_one_byte
 
 453         cjne r2, #0x04, setup_ctrl_not_04
 
 454         ;; 04 is send break (really "turn break on/off"). TODO
 
 455         cjne r3, #0x00, setup_ctrl_do_break_on
 
 456         ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
 
 462 setup_ctrl_do_break_on:
 
 463         ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
 
 474         cjne r2, #0x05, setup_ctrl_not_05
 
 475         ;; 05 is set desired interrupt bitmap. TODO
 
 478         cjne r2, #0x06, setup_ctrl_not_06
 
 480         cjne r3, #0x00, setup_ctrl_06_not_00
 
 481         ;; 06, wValue[0]=0 is query write_room
 
 484         subb a, tx_ring_in      ; out-1-in = 255 - (in-out)
 
 485         ljmp setup_return_one_byte
 
 486 setup_ctrl_06_not_00:
 
 487         cjne r3, #0x01, setup_ctrl_06_not_01
 
 488         ;; 06, wValue[0]=1 is query chars_in_buffer
 
 491         subb a, tx_ring_out     ; in-out
 
 492         ljmp setup_return_one_byte
 
 493 setup_ctrl_06_not_01:   
 
 496         cjne r2, #0x07, setup_ctrl_not_07
 
 497         ;; 07 is request tx unthrottle interrupt
 
 498         mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
 
 503 setup_bmreq_type_not_vendor:
 
 507 setup_bmreq_is_standard:        
 
 508         cjne r2, #0x00, setup_breq_not_00
 
 509         ;; 00:  Get_Status (sub-switch on bmRequestType: device, ep, int)
 
 510         cjne r1, #0x80, setup_Get_Status_not_device
 
 511         ;; Get_Status(device)
 
 512         ;;  are we self-powered? no. can we do remote wakeup? no
 
 513         ;;   so return two zero bytes. This is reusable
 
 514 setup_return_two_zero_bytes:
 
 524 setup_Get_Status_not_device:
 
 525         cjne r1, #0x82, setup_Get_Status_not_endpoint
 
 526         ;; Get_Status(endpoint)
 
 527         ;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
 
 528         ;; for now: cheat. TODO
 
 529         sjmp setup_return_two_zero_bytes
 
 530 setup_Get_Status_not_endpoint:
 
 531         cjne r1, #0x81, setup_Get_Status_not_interface
 
 532         ;; Get_Status(interface): return two zeros
 
 533         sjmp setup_return_two_zero_bytes
 
 534 setup_Get_Status_not_interface: 
 
 538         cjne r2, #0x01, setup_breq_not_01
 
 539         ;; 01:  Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
 
 540         cjne r3, #0x00, setup_Clear_Feature_not_stall
 
 541         ;; Clear_Feature(stall). should clear a stall bit. TODO
 
 543 setup_Clear_Feature_not_stall:
 
 544         cjne r3, #0x01, setup_Clear_Feature_not_rwake
 
 545         ;; Clear_Feature(remote wakeup). ignored.
 
 547 setup_Clear_Feature_not_rwake:
 
 551         cjne r2, #0x03, setup_breq_not_03
 
 552         ;; 03:  Set_Feature (sub-switch on wValueL: stall, remote wakeup)
 
 553         cjne r3, #0x00, setup_Set_Feature_not_stall
 
 554         ;; Set_Feature(stall). Should set a stall bit. TODO
 
 556 setup_Set_Feature_not_stall:
 
 557         cjne r3, #0x01, setup_Set_Feature_not_rwake
 
 558         ;; Set_Feature(remote wakeup). ignored.
 
 560 setup_Set_Feature_not_rwake:
 
 564         cjne r2, #0x06, setup_breq_not_06
 
 565         ;; 06:  Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
 
 566         cjne r4, #0x01, setup_Get_Descriptor_not_device
 
 567         ;; Get_Descriptor(device)
 
 569         mov a, #HIGH(desc_device)
 
 572         mov a, #LOW(desc_device)
 
 575 setup_Get_Descriptor_not_device:
 
 576         cjne r4, #0x02, setup_Get_Descriptor_not_config
 
 577         ;; Get_Descriptor(config[n])
 
 578         cjne r3, #0x00, setup_stall; only handle n==0
 
 579         ;; Get_Descriptor(config[0])
 
 581         mov a, #HIGH(desc_config1)
 
 584         mov a, #LOW(desc_config1)
 
 587 setup_Get_Descriptor_not_config:
 
 588         cjne r4, #0x03, setup_Get_Descriptor_not_string
 
 589         ;; Get_Descriptor(string[wValueL])
 
 590         ;;  if (wValueL >= maxstrings) stall
 
 591         mov a, #((desc_strings_end-desc_strings)/2)
 
 593         subb a,r3               ; a=4, r3 = 0..3 . if a<=0 then stall
 
 597         add a, r3               ; a = 2*wValueL
 
 598         mov dptr, #desc_strings
 
 603         mov dph, a              ; dph = desc_strings[a]. big endian! (handy)
 
 604         ;; it looks like my adapter uses a revision of the EZUSB that
 
 605         ;; contains "rev D errata number 8", as hinted in the EzUSB example
 
 606         ;; code. I cannot find an actual errata description on the Cypress
 
 607         ;; web site, but from the example code it looks like this bug causes
 
 608         ;; the length of string descriptors to be read incorrectly, possibly
 
 609         ;; sending back more characters than the descriptor has. The workaround
 
 610         ;; is to manually send out all of the data. The consequence of not
 
 611         ;; using the workaround is that the strings gathered by the kernel
 
 612         ;; driver are too long and are filled with trailing garbage (including
 
 613         ;; leftover strings). Writing this out by hand is a nuisance, so for
 
 614         ;; now I will just live with the bug.
 
 629 setup_Get_Descriptor_not_string:
 
 633         cjne r2, #0x08, setup_breq_not_08
 
 634         ;; Get_Configuration. always 1. return one byte.
 
 637 setup_return_one_byte:  
 
 645         cjne r2, #0x09, setup_breq_not_09
 
 646         ;; 09: Set_Configuration. ignored.
 
 649         cjne r2, #0x0a, setup_breq_not_0a
 
 650         ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
 
 651         ;;  since we only have one interface, ignore wIndexL, return a 0
 
 653         ljmp setup_return_one_byte
 
 655         cjne r2, #0x0b, setup_breq_not_0b
 
 656         ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
 
 686 ;;; ==============================================================
 
 688 set_baud:                       ; baud index in r3
 
 691         jb ACC.7, set_baud__badbaud
 
 694         jnc set_baud__badbaud
 
 697         add a, #LOW(baud_table)
 
 699         mov a, #HIGH(baud_table)
 
 702         ;; TODO: shut down xmit/receive
 
 703         ;; TODO: wait for current xmit char to leave
 
 704         ;; TODO: shut down timer to avoid partial-char glitch
 
 705         movx a,@dptr            ; BAUD_HIGH
 
 709         movx a,@dptr            ; BAUD_LOW
 
 712         ;; TODO: restart xmit/receive
 
 713         ;; TODO: reenable interrupts, resume tx if pending
 
 717         setb c                  ; c=1: failure
 
 720 ;;; ==================================================
 
 722         cjne r1, #0x41, control_pins_in
 
 725         mov a, r3 ; wValue[0] holds new bits:   b7 is new RTS
 
 726         xrl a, #0xff            ; 1 means active, 0V, +12V ?
 
 730         movx a, @dptr           ; only change bit 7 
 
 733         movx @dptr, a           ; other pins are inputs, bits ignored
 
 739         ljmp setup_return_one_byte
 
 741 ;;; ========================================
 
 752         mov EXIF,a              ; clear INT2 first
 
 753         mov dptr, IN07IRQ       ; clear USB int
 
 757         mov a, #0x20             ; Turn off the green LED
 
 765         mov a, #0x20             ; Turn off the green LED
 
 787         mov a, #0x10             ; Turn the green LED
 
 795         mov EXIF,a              ; clear INT2 first
 
 796         mov dptr, OUT07IRQ      ; clear USB int
 
 802         ;; copy data into buffer. for now, assume we will have enough space
 
 803         mov dptr, OUT2BC        ; get byte count
 
 808         mov dptr, OUT2BUF       ; load DPTR0 with source
 
 809         mov dph1, #HIGH(tx_ring)        ; load DPTR1 with target
 
 813         inc dps                 ; switch to DPTR1: target
 
 814         inc dpl1                ; target = tx_ring_in+1
 
 817         cjne a, tx_ring_out, OUT_no_overflow
 
 820         inc tx_ring_in          ; tx_ring_in++
 
 821         inc dps                 ; switch to DPTR0: source
 
 837         mov a, #0x20             ; Turn off the green LED
 
 850         ;; fill in EP4in with a debugging message:
 
 851         ;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
 
 861         jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
 
 879         jnb TX_RUNNING, dump_stat__no_tx_running
 
 881 dump_stat__no_tx_running:
 
 886         mov dptr, #tx_ring      ; DPTR1: source
 
 888 dump_stat__tx_ring_loop:
 
 895         djnz r1, dump_stat__tx_ring_loop
 
 904         mov dptr, #rx_ring      ; DPTR1: source
 
 906 dump_stat__rx_ring_loop:
 
 913         djnz r1, dump_stat__rx_ring_loop
 
 924 ;;; ============================================================
 
 927         ;; make sure the tx process is running.
 
 928         jb TX_RUNNING, start_tx_done
 
 930         ;; is there work to be done?
 
 932         cjne a,tx_ring_out, start_tx__work
 
 935         ;; tx was not running. send the first character, setup the TI int
 
 936         inc tx_ring_out         ; [++tx_ring_out]
 
 937         mov dph, #HIGH(tx_ring)
 
 943         ;; can we unthrottle the host tx process?
 
 944         ;;  step 1: do we care?
 
 946         cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
 
 948 start_tx_really_done:
 
 950 start_tx__maybe_unthrottle_tx:
 
 951         ;;  step 2: is there now room?
 
 955         ;; a is now write_room. If thresh >= a, we can unthrottle
 
 957         subb a, tx_unthrottle_threshold
 
 958         jc start_tx_really_done ; nope
 
 959         ;; yes, we can unthrottle. remove the threshold and mark a request
 
 960         mov tx_unthrottle_threshold, #0
 
 961         setb DO_TX_UNTHROTTLE
 
 962         ;; prod rx, which will actually send the message when in2 becomes free
 
 973         jnb TI, serial_int__not_tx
 
 974         ;; tx finished. send another character if we have one
 
 979         jnb RI, serial_int__not_rx
 
 993         mov dph, #HIGH(rx_ring)
 
 995         inc dpl                 ; target = rx_ring_in+1
 
 998         ;; check for overflow before incrementing rx_ring_in
 
1000         cjne a, rx_ring_out, get_rx_char__no_overflow
 
1003 get_rx_char__no_overflow:       
 
1005         ;; kick off USB INpipe
 
1010         ;; check if the inpipe is already running.
 
1017         jb acc.1, start_in__done; int will handle it
 
1018         jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
 
1019         ;; see if there is any work to do. a serial interrupt might occur
 
1020         ;; during this sequence?
 
1022         cjne a, rx_ring_out, start_in__have_work
 
1024 start_in__have_work:    
 
1025         ;; now copy as much data as possible into the pipe. 63 bytes max.
 
1028         mov dph, #HIGH(rx_ring) ; load DPTR0 with source
 
1030         mov dptr, IN2BUF        ; load DPTR1 with target
 
1031         movx @dptr, a           ; in[0] signals that rest of IN is rx data
 
1034         ;; loop until we run out of data, or we have copied 64 bytes
 
1035         mov r1, #1              ; INbuf size counter
 
1038         cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
 
1040 start_inlocal_irq_enablell_copying:
 
1042         mov dpl, rx_ring_out
 
1045         movx @dptr, a           ; write into IN buffer
 
1049         cjne r1, #64, start_in__loop; loop
 
1051         ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
 
1053         mov a, #0x10             ; Turn the green LED
 
1064 start_in__do_tx_unthrottle:
 
1065         ;; special sequence: send a tx unthrottle message
 
1066         clr DO_TX_UNTHROTTLE
 
1083         jnb TI, putchar_wait
 
1088 baud_table:                     ; baud_high, then baud_low
 
1090         .byte BAUD_HIGH(110)
 
1093         .byte BAUD_HIGH(300)
 
1096         .byte BAUD_HIGH(1200)
 
1097         .byte BAUD_LOW(1200)
 
1099         .byte BAUD_HIGH(2400)
 
1100         .byte BAUD_LOW(2400)
 
1102         .byte BAUD_HIGH(4800)
 
1103         .byte BAUD_LOW(4800)
 
1105         .byte BAUD_HIGH(9600)
 
1106         .byte BAUD_LOW(9600)
 
1108         .byte BAUD_HIGH(19200)
 
1109         .byte BAUD_LOW(19200)
 
1111         .byte BAUD_HIGH(38400)
 
1112         .byte BAUD_LOW(38400)
 
1114         .byte BAUD_HIGH(57600)
 
1115         .byte BAUD_LOW(57600)
 
1117         .byte BAUD_HIGH(115200)
 
1118         .byte BAUD_LOW(115200)
 
1121         .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
 
1122         .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
 
1123 ;;; The "real" device id, which must match the host driver, is that
 
1124 ;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
 
1127         .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
 
1128         .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
 
1129         .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
 
1130         .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
 
1133         .word string_langids, string_mfg, string_product, string_serial
 
1136 string_langids: .byte string_langids_end-string_langids
 
1141         ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
 
1142         ;; *that* is a pain in the ass to encode. And they are little-endian
 
1143         ;; too. Use this perl snippet to get the bytecodes:
 
1147              printf("0x%02x, 0x00, ", ord($c));
 
1152 string_mfg:     .byte string_mfg_end-string_mfg
 
1154 ;       .byte "ACME usb widgets"
 
1155         .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
 
1158 string_product: .byte string_product_end-string_product
 
1160 ;       .byte "ACME USB serial widget"
 
1161         .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
 
1164 string_serial:  .byte string_serial_end-string_serial
 
1167         .byte 0x34, 0x00, 0x37, 0x00
 
1170 ;;; ring buffer memory
 
1171         ;; tx_ring_in+1 is where the next input byte will go
 
1172         ;; [tx_ring_out] has been sent
 
1173         ;; if tx_ring_in == tx_ring_out, theres no work to do
 
1174         ;; there are (tx_ring_in - tx_ring_out) chars to be written
 
1175         ;; dont let _in lap _out
 
1176         ;;   cannot inc if tx_ring_in+1 == tx_ring_out
 
1177         ;;  write [tx_ring_in+1] then tx_ring_in++
 
1178         ;;   if (tx_ring_in+1 == tx_ring_out), overflow
 
1179         ;;   else tx_ring_in++
 
1180         ;;  read/send [tx_ring_out+1], then tx_ring_out++
 
1182         ;; rx_ring_in works the same way
 
1186         .skip 0x100             ; 256 bytes
 
1188         .skip 0x100             ; 256 bytes