linux/firmware/keyspan_pda/keyspan_pda.S
<<
>>
Prefs
   1/*  $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
   2 * 
   3 *  Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
   4 *  the EzUSB microcontroller.
   5 * 
   6 *  (C) Copyright 2000 Brian Warner <warner@lothar.com>
   7 * 
   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.
  12 * 
  13 *  "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
  14 *  company.
  15 * 
  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
  25 *
  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.
  32 * 
  33 * THEORY OF OPERATION:
  34 *
  35 *   There are two 256-byte ring buffers, one for tx, one for rx.
  36 *
  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.
  44 *
  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.
  49 *
  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.
  54 *
  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:
  59 *
  60 *    bRequest = 0 : set baud/bits/parity
  61 *               1 : unused
  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
  68 *
  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.
  75 *
  76 * USEFUL DOCS:
  77 *  EzUSB Technical Reference Manual: <http://www.cypress.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
  82 *
  83 * HOW TO BUILD:
  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.
  90 *
  91 * THANKS:
  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.
  96 *
  97 * TODO:
  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.
 107 *
 108 * Questions, bugs, patches to Brian.
 109 *
 110 *  -Brian Warner <warner@lothar.com>
 111 *
 112 */
 113        
 114#define HIGH(x) (((x) & 0xff00) / 256)
 115#define LOW(x) ((x) & 0xff)
 116
 117#define dpl1 0x84
 118#define dph1 0x85
 119#define dps 0x86
 120
 121;;; our bit assignments
 122#define TX_RUNNING 0
 123#define DO_TX_UNTHROTTLE 1
 124        
 125        ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
 126#define STACK #0x60-1
 127
 128#define EXIF 0x91
 129#define EIE 0xe8
 130        .flag EUSB, EIE.0
 131        .flag ES0, IE.4
 132
 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
 148#define OEB #0x7f9d
 149#define OUTB #0x7f97
 150#define OEC #0x7f9e
 151#define OUTC #0x7f98
 152#define PINSC #0x7f9b
 153#define PORTCCFG #0x7f95
 154#define IN07IRQ #0x7fa9
 155#define OUT07IRQ #0x7faa
 156#define IN07IEN #0x7fac
 157#define OUT07IEN #0x7fad
 158#define USBIRQ #0x7fab
 159#define USBIEN #0x7fae
 160#define USBBAV #0x7faf
 161#define USBCS #0x7fd6
 162#define SUDPTRH #0x7fd4
 163#define SUDPTRL #0x7fd5
 164#define SETUPDAT #0x7fe8
 165                
 166        ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
 167
 168        .org 0
 169        ljmp start
 170        ;; interrupt vectors
 171        .org 23H
 172        ljmp serial_int
 173        .byte 0
 174        
 175        .org 43H
 176        ljmp USB_Jump_Table
 177        .byte 0                 ; filled in by the USB core
 178
 179;;; local variables. These are not initialized properly: do it by hand.
 180        .org 30H
 181rx_ring_in:     .byte 0
 182rx_ring_out:    .byte 0
 183tx_ring_in:     .byte 0
 184tx_ring_out:    .byte 0
 185tx_unthrottle_threshold:        .byte 0
 186                
 187        .org 0x100H             ; wants to be on a page boundary
 188USB_Jump_Table:
 189        ljmp    ISR_Sudav       ; Setup Data Available
 190        .byte 0
 191        ljmp    0               ; Start of Frame
 192        .byte 0
 193        ljmp    0               ; Setup Data Loading
 194        .byte 0
 195        ljmp    0               ; Global Suspend
 196        .byte   0
 197        ljmp    0               ; USB Reset     
 198        .byte   0
 199        ljmp    0               ; Reserved
 200        .byte   0
 201        ljmp    0               ; End Point 0 In
 202        .byte   0
 203        ljmp    0               ; End Point 0 Out
 204        .byte   0
 205        ljmp    0               ; End Point 1 In
 206        .byte   0
 207        ljmp    0               ; End Point 1 Out
 208        .byte   0
 209        ljmp    ISR_Ep2in
 210        .byte   0
 211        ljmp    ISR_Ep2out
 212        .byte   0
 213
 214
 215        .org 0x200
 216                
 217start:  mov SP,STACK-1 ; set stack
 218        ;; clear local variables
 219        clr a
 220        mov tx_ring_in, a
 221        mov tx_ring_out, a
 222        mov rx_ring_in, a
 223        mov rx_ring_out, a
 224        mov tx_unthrottle_threshold, a
 225        clr TX_RUNNING
 226        clr DO_TX_UNTHROTTLE
 227        
 228        ;; clear fifo with "fe"
 229        mov r1, 0
 230        mov a, #0xfe
 231        mov dptr, #tx_ring
 232clear_tx_ring_loop:
 233        movx @dptr, a
 234        inc dptr
 235        djnz r1, clear_tx_ring_loop
 236
 237        mov a, #0xfd
 238        mov dptr, #rx_ring
 239clear_rx_ring_loop:
 240        movx @dptr, a
 241        inc dptr
 242        djnz r1, clear_rx_ring_loop
 243
 244;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
 245        ;; set OEB.1
 246        mov a, #02H
 247        mov dptr,OEB
 248        movx @dptr,a
 249        ;; clear PB1
 250        mov a, #00H
 251        mov dptr,OUTB
 252        movx @dptr,a
 253        ;; set OEC.[127]
 254        mov a, #0x86
 255        mov dptr,OEC
 256        movx @dptr,a
 257        ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
 258        mov dptr, PORTCCFG
 259        mov a, #0x03
 260        movx @dptr, a
 261        
 262        ;; set up interrupts, autovectoring
 263        mov dptr, USBBAV
 264        movx a,@dptr
 265        setb acc.0              ; AVEN bit to 0
 266        movx @dptr, a
 267
 268        mov a,#0x01             ; enable SUDAV: setup data available (for ep0)
 269        mov dptr, USBIRQ
 270        movx @dptr, a           ; clear SUDAVI
 271        mov dptr, USBIEN
 272        movx @dptr, a
 273        
 274        mov dptr, IN07IEN
 275        mov a,#0x04             ; enable IN2 int
 276        movx @dptr, a
 277        
 278        mov dptr, OUT07IEN
 279        mov a,#0x04             ; enable OUT2 int
 280        movx @dptr, a
 281        mov dptr, OUT2BC
 282        movx @dptr, a           ; arm OUT2
 283
 284        mov a, #0x84            ; turn on RTS, DTR
 285        mov dptr,OUTC
 286        movx @dptr, a
 287        ;; setup the serial port. 9600 8N1.
 288        mov a,#01010011         ; mode 1, enable rx, clear int
 289        mov SCON, a
 290        ;;  using timer2, in 16-bit baud-rate-generator mode
 291        ;;   (xtal 12MHz, internal fosc 24MHz)
 292        ;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
 293        ;;  57600: 0xFFF2.F, say 0xFFF3
 294        ;;   9600: 0xFFB1.E, say 0xFFB2
 295        ;;    300: 0xF63C
 296#define BAUD 9600
 297#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
 298#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
 299#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
 300                
 301        mov T2CON, #030h        ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
 302        mov r3, #5
 303        acall set_baud
 304        setb TR2
 305        mov SCON, #050h
 306        
 307#if 0
 308        mov r1, #0x40
 309        mov a, #0x41
 310send:   
 311        mov SBUF, a
 312        inc a
 313        anl a, #0x3F
 314        orl a, #0x40
 315;       xrl a, #0x02
 316wait1:  
 317        jnb TI, wait1
 318        clr TI
 319        djnz r1, send
 320;done:  sjmp done
 321
 322#endif
 323        
 324        setb EUSB
 325        setb EA
 326        setb ES0
 327        ;acall dump_stat
 328
 329        ;; hey, what say we RENUMERATE! (TRM p.62)
 330        mov a, #0
 331        mov dps, a
 332        mov dptr, USBCS
 333        mov a, #0x02            ; DISCON=0, DISCOE=0, RENUM=1
 334        movx @dptr, a
 335        ;; now presence pin is floating, simulating disconnect. wait 0.5s
 336        mov r1, #46
 337renum_wait1:
 338        mov r2, #0
 339renum_wait2:
 340        mov r3, #0
 341renum_wait3:
 342        djnz r3, renum_wait3
 343        djnz r2, renum_wait2
 344        djnz r1, renum_wait1    ; wait about n*(256^2) 6MHz clocks
 345        mov a, #0x06            ; DISCON=0, DISCOE=1, RENUM=1
 346        movx @dptr, a
 347        ;; we are back online. the host device will now re-query us
 348        
 349        
 350main:   sjmp main
 351
 352        
 353
 354ISR_Sudav:
 355        push dps
 356        push dpl
 357        push dph
 358        push dpl1
 359        push dph1
 360        push acc
 361        mov a,EXIF
 362        clr acc.4
 363        mov EXIF,a              ; clear INT2 first
 364        mov dptr, USBIRQ        ; clear USB int
 365        mov a,#01h
 366        movx @dptr,a
 367
 368        ;; get request type
 369        mov dptr, SETUPDAT
 370        movx a, @dptr
 371        mov r1, a               ; r1 = bmRequestType
 372        inc dptr
 373        movx a, @dptr
 374        mov r2, a               ; r2 = bRequest
 375        inc dptr
 376        movx a, @dptr
 377        mov r3, a               ; r3 = wValueL
 378        inc dptr
 379        movx a, @dptr
 380        mov r4, a               ; r4 = wValueH
 381
 382        ;; main switch on bmRequest.type: standard or vendor
 383        mov a, r1
 384        anl a, #0x60
 385        cjne a, #0x00, setup_bmreq_type_not_standard
 386        ;; standard request: now main switch is on bRequest
 387        ljmp setup_bmreq_is_standard
 388        
 389setup_bmreq_type_not_standard:  
 390        ;; a still has bmreq&0x60
 391        cjne a, #0x40, setup_bmreq_type_not_vendor
 392        ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
 393        ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
 394        cjne r2, #0x00, setup_ctrl_not_00
 395        ;; 00 is set baud, wValue[0] has baud rate index
 396        lcall set_baud          ; index in r3, carry set if error
 397        jc setup_bmreq_type_not_standard__do_stall
 398        ljmp setup_done_ack
 399setup_bmreq_type_not_standard__do_stall:
 400        ljmp setup_stall
 401setup_ctrl_not_00:
 402        cjne r2, #0x01, setup_ctrl_not_01
 403        ;; 01 is reserved for set bits (parity). TODO
 404        ljmp setup_stall
 405setup_ctrl_not_01:
 406        cjne r2, #0x02, setup_ctrl_not_02
 407        ;; 02 is set HW flow control. TODO
 408        ljmp setup_stall
 409setup_ctrl_not_02:
 410        cjne r2, #0x03, setup_ctrl_not_03
 411        ;; 03 is control pins (RTS, DTR).
 412        ljmp control_pins       ; will jump to setup_done_ack,
 413                                ;  or setup_return_one_byte
 414setup_ctrl_not_03:
 415        cjne r2, #0x04, setup_ctrl_not_04
 416        ;; 04 is send break (really "turn break on/off"). TODO
 417        cjne r3, #0x00, setup_ctrl_do_break_on
 418        ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
 419        mov dptr, PORTCCFG
 420        movx a, @dptr
 421        orl a, #0x02
 422        movx @dptr, a
 423        ljmp setup_done_ack
 424setup_ctrl_do_break_on:
 425        ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
 426        mov dptr, OUTC
 427        movx a, @dptr
 428        anl a, #0xfd            ; ~0x02
 429        movx @dptr, a
 430        mov dptr, PORTCCFG
 431        movx a, @dptr
 432        anl a, #0xfd            ; ~0x02
 433        movx @dptr, a
 434        ljmp setup_done_ack
 435setup_ctrl_not_04:
 436        cjne r2, #0x05, setup_ctrl_not_05
 437        ;; 05 is set desired interrupt bitmap. TODO
 438        ljmp setup_stall
 439setup_ctrl_not_05:
 440        cjne r2, #0x06, setup_ctrl_not_06
 441        ;; 06 is query room
 442        cjne r3, #0x00, setup_ctrl_06_not_00
 443        ;; 06, wValue[0]=0 is query write_room
 444        mov a, tx_ring_out
 445        setb c
 446        subb a, tx_ring_in      ; out-1-in = 255 - (in-out)
 447        ljmp setup_return_one_byte
 448setup_ctrl_06_not_00:
 449        cjne r3, #0x01, setup_ctrl_06_not_01
 450        ;; 06, wValue[0]=1 is query chars_in_buffer
 451        mov a, tx_ring_in
 452        clr c
 453        subb a, tx_ring_out     ; in-out
 454        ljmp setup_return_one_byte
 455setup_ctrl_06_not_01:   
 456        ljmp setup_stall
 457setup_ctrl_not_06:
 458        cjne r2, #0x07, setup_ctrl_not_07
 459        ;; 07 is request tx unthrottle interrupt
 460        mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
 461        ljmp setup_done_ack
 462setup_ctrl_not_07:
 463        ljmp setup_stall
 464        
 465setup_bmreq_type_not_vendor:
 466        ljmp setup_stall
 467
 468
 469setup_bmreq_is_standard:        
 470        cjne r2, #0x00, setup_breq_not_00
 471        ;; 00:  Get_Status (sub-switch on bmRequestType: device, ep, int)
 472        cjne r1, #0x80, setup_Get_Status_not_device
 473        ;; Get_Status(device)
 474        ;;  are we self-powered? no. can we do remote wakeup? no
 475        ;;   so return two zero bytes. This is reusable
 476setup_return_two_zero_bytes:
 477        mov dptr, IN0BUF
 478        clr a
 479        movx @dptr, a
 480        inc dptr
 481        movx @dptr, a
 482        mov dptr, IN0BC
 483        mov a, #2
 484        movx @dptr, a
 485        ljmp setup_done_ack
 486setup_Get_Status_not_device:
 487        cjne r1, #0x82, setup_Get_Status_not_endpoint
 488        ;; Get_Status(endpoint)
 489        ;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
 490        ;; for now: cheat. TODO
 491        sjmp setup_return_two_zero_bytes
 492setup_Get_Status_not_endpoint:
 493        cjne r1, #0x81, setup_Get_Status_not_interface
 494        ;; Get_Status(interface): return two zeros
 495        sjmp setup_return_two_zero_bytes
 496setup_Get_Status_not_interface: 
 497        ljmp setup_stall
 498        
 499setup_breq_not_00:
 500        cjne r2, #0x01, setup_breq_not_01
 501        ;; 01:  Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
 502        cjne r3, #0x00, setup_Clear_Feature_not_stall
 503        ;; Clear_Feature(stall). should clear a stall bit. TODO
 504        ljmp setup_stall
 505setup_Clear_Feature_not_stall:
 506        cjne r3, #0x01, setup_Clear_Feature_not_rwake
 507        ;; Clear_Feature(remote wakeup). ignored.
 508        ljmp setup_done_ack
 509setup_Clear_Feature_not_rwake:
 510        ljmp setup_stall
 511        
 512setup_breq_not_01:
 513        cjne r2, #0x03, setup_breq_not_03
 514        ;; 03:  Set_Feature (sub-switch on wValueL: stall, remote wakeup)
 515        cjne r3, #0x00, setup_Set_Feature_not_stall
 516        ;; Set_Feature(stall). Should set a stall bit. TODO
 517        ljmp setup_stall
 518setup_Set_Feature_not_stall:
 519        cjne r3, #0x01, setup_Set_Feature_not_rwake
 520        ;; Set_Feature(remote wakeup). ignored.
 521        ljmp setup_done_ack
 522setup_Set_Feature_not_rwake:
 523        ljmp setup_stall
 524        
 525setup_breq_not_03:      
 526        cjne r2, #0x06, setup_breq_not_06
 527        ;; 06:  Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
 528        cjne r4, #0x01, setup_Get_Descriptor_not_device
 529        ;; Get_Descriptor(device)
 530        mov dptr, SUDPTRH
 531        mov a, #HIGH(desc_device)
 532        movx @dptr, a
 533        mov dptr, SUDPTRL
 534        mov a, #LOW(desc_device)
 535        movx @dptr, a
 536        ljmp setup_done_ack
 537setup_Get_Descriptor_not_device:
 538        cjne r4, #0x02, setup_Get_Descriptor_not_config
 539        ;; Get_Descriptor(config[n])
 540        cjne r3, #0x00, setup_stall; only handle n==0
 541        ;; Get_Descriptor(config[0])
 542        mov dptr, SUDPTRH
 543        mov a, #HIGH(desc_config1)
 544        movx @dptr, a
 545        mov dptr, SUDPTRL
 546        mov a, #LOW(desc_config1)
 547        movx @dptr, a
 548        ljmp setup_done_ack
 549setup_Get_Descriptor_not_config:
 550        cjne r4, #0x03, setup_Get_Descriptor_not_string
 551        ;; Get_Descriptor(string[wValueL])
 552        ;;  if (wValueL >= maxstrings) stall
 553        mov a, #((desc_strings_end-desc_strings)/2)
 554        clr c
 555        subb a,r3               ; a=4, r3 = 0..3 . if a<=0 then stall
 556        jc  setup_stall
 557        jz  setup_stall
 558        mov a, r3
 559        add a, r3               ; a = 2*wValueL
 560        mov dptr, #desc_strings
 561        add a, dpl
 562        mov dpl, a
 563        mov a, #0
 564        addc a, dph
 565        mov dph, a              ; dph = desc_strings[a]. big endian! (handy)
 566        ;; it looks like my adapter uses a revision of the EZUSB that
 567        ;; contains "rev D errata number 8", as hinted in the EzUSB example
 568        ;; code. I cannot find an actual errata description on the Cypress
 569        ;; web site, but from the example code it looks like this bug causes
 570        ;; the length of string descriptors to be read incorrectly, possibly
 571        ;; sending back more characters than the descriptor has. The workaround
 572        ;; is to manually send out all of the data. The consequence of not
 573        ;; using the workaround is that the strings gathered by the kernel
 574        ;; driver are too long and are filled with trailing garbage (including
 575        ;; leftover strings). Writing this out by hand is a nuisance, so for
 576        ;; now I will just live with the bug.
 577        movx a, @dptr
 578        mov r1, a
 579        inc dptr
 580        movx a, @dptr
 581        mov r2, a
 582        mov dptr, SUDPTRH
 583        mov a, r1
 584        movx @dptr, a
 585        mov dptr, SUDPTRL
 586        mov a, r2
 587        movx @dptr, a
 588        ;; done
 589        ljmp setup_done_ack
 590        
 591setup_Get_Descriptor_not_string:
 592        ljmp setup_stall
 593        
 594setup_breq_not_06:
 595        cjne r2, #0x08, setup_breq_not_08
 596        ;; Get_Configuration. always 1. return one byte.
 597        ;; this is reusable
 598        mov a, #1
 599setup_return_one_byte:  
 600        mov dptr, IN0BUF
 601        movx @dptr, a
 602        mov a, #1
 603        mov dptr, IN0BC
 604        movx @dptr, a
 605        ljmp setup_done_ack
 606setup_breq_not_08:
 607        cjne r2, #0x09, setup_breq_not_09
 608        ;; 09: Set_Configuration. ignored.
 609        ljmp setup_done_ack
 610setup_breq_not_09:
 611        cjne r2, #0x0a, setup_breq_not_0a
 612        ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
 613        ;;  since we only have one interface, ignore wIndexL, return a 0
 614        mov a, #0
 615        ljmp setup_return_one_byte
 616setup_breq_not_0a:
 617        cjne r2, #0x0b, setup_breq_not_0b
 618        ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
 619        ljmp setup_done_ack
 620setup_breq_not_0b:
 621        ljmp setup_stall
 622
 623                
 624setup_done_ack: 
 625        ;; now clear HSNAK
 626        mov dptr, EP0CS
 627        mov a, #0x02
 628        movx @dptr, a
 629        sjmp setup_done
 630setup_stall:    
 631        ;; unhandled. STALL
 632        ;EP0CS |= bmEPSTALL
 633        mov dptr, EP0CS
 634        movx a, @dptr
 635        orl a, EP0STALLbit
 636        movx @dptr, a
 637        sjmp setup_done
 638        
 639setup_done:     
 640        pop acc
 641        pop dph1
 642        pop dpl1
 643        pop dph
 644        pop dpl
 645        pop dps
 646        reti
 647
 648;;; ==============================================================
 649        
 650set_baud:                       ; baud index in r3
 651        ;; verify a < 10
 652        mov a, r3
 653        jb ACC.7, set_baud__badbaud
 654        clr c
 655        subb a, #10
 656        jnc set_baud__badbaud
 657        mov a, r3
 658        rl a                    ; a = index*2
 659        add a, #LOW(baud_table)
 660        mov dpl, a
 661        mov a, #HIGH(baud_table)
 662        addc a, #0
 663        mov dph, a
 664        ;; TODO: shut down xmit/receive
 665        ;; TODO: wait for current xmit char to leave
 666        ;; TODO: shut down timer to avoid partial-char glitch
 667        movx a,@dptr            ; BAUD_HIGH
 668        mov RCAP2H, a
 669        mov TH2, a
 670        inc dptr
 671        movx a,@dptr            ; BAUD_LOW
 672        mov RCAP2L, a
 673        mov TL2, a
 674        ;; TODO: restart xmit/receive
 675        ;; TODO: reenable interrupts, resume tx if pending
 676        clr c                   ; c=0: success
 677        ret
 678set_baud__badbaud:
 679        setb c                  ; c=1: failure
 680        ret
 681        
 682;;; ==================================================
 683control_pins:
 684        cjne r1, #0x41, control_pins_in
 685control_pins_out:
 686        mov a, r3 ; wValue[0] holds new bits:   b7 is new DTR, b2 is new RTS
 687        xrl a, #0xff            ; 1 means active, 0V, +12V ?
 688        anl a, #0x84
 689        mov r3, a
 690        mov dptr, OUTC
 691        movx a, @dptr           ; only change bits 7 and 2
 692        anl a, #0x7b            ; ~0x84
 693        orl a, r3
 694        movx @dptr, a           ; other pins are inputs, bits ignored
 695        ljmp setup_done_ack
 696control_pins_in:
 697        mov dptr, PINSC
 698        movx a, @dptr
 699        xrl a, #0xff
 700        ljmp setup_return_one_byte
 701
 702;;; ========================================
 703        
 704ISR_Ep2in:
 705        push dps
 706        push dpl
 707        push dph
 708        push dpl1
 709        push dph1
 710        push acc
 711        mov a,EXIF
 712        clr acc.4
 713        mov EXIF,a              ; clear INT2 first
 714        mov dptr, IN07IRQ       ; clear USB int
 715        mov a,#04h
 716        movx @dptr,a
 717
 718        ;; do stuff
 719        lcall start_in
 720        
 721        pop acc
 722        pop dph1
 723        pop dpl1
 724        pop dph
 725        pop dpl
 726        pop dps
 727        reti
 728
 729ISR_Ep2out:
 730        push dps
 731        push dpl
 732        push dph
 733        push dpl1
 734        push dph1
 735        push acc
 736        mov a,EXIF
 737        clr acc.4
 738        mov EXIF,a              ; clear INT2 first
 739        mov dptr, OUT07IRQ      ; clear USB int
 740        mov a,#04h
 741        movx @dptr,a
 742
 743        ;; do stuff
 744
 745        ;; copy data into buffer. for now, assume we will have enough space
 746        mov dptr, OUT2BC        ; get byte count
 747        movx a,@dptr
 748        mov r1, a
 749        clr a
 750        mov dps, a
 751        mov dptr, OUT2BUF       ; load DPTR0 with source
 752        mov dph1, #HIGH(tx_ring)        ; load DPTR1 with target
 753        mov dpl1, tx_ring_in
 754OUT_loop:
 755        movx a,@dptr            ; read
 756        inc dps                 ; switch to DPTR1: target
 757        inc dpl1                ; target = tx_ring_in+1
 758        movx @dptr,a            ; store
 759        mov a,dpl1
 760        cjne a, tx_ring_out, OUT_no_overflow
 761        sjmp OUT_overflow
 762OUT_no_overflow:        
 763        inc tx_ring_in          ; tx_ring_in++
 764        inc dps                 ; switch to DPTR0: source
 765        inc dptr
 766        djnz r1, OUT_loop
 767        sjmp OUT_done
 768OUT_overflow:
 769        ;; signal overflow
 770        ;; fall through
 771OUT_done:       
 772        ;; ack
 773        mov dptr,OUT2BC
 774        movx @dptr,a
 775
 776        ;; start tx
 777        acall maybe_start_tx
 778        ;acall dump_stat
 779        
 780        pop acc
 781        pop dph1
 782        pop dpl1
 783        pop dph
 784        pop dpl
 785        pop dps
 786        reti
 787
 788dump_stat:
 789        ;; fill in EP4in with a debugging message:
 790        ;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
 791        ;;   tx_active
 792        ;;   tx_ring[0..15]
 793        ;;   0xfc
 794        ;;   rx_ring[0..15]
 795        clr a
 796        mov dps, a
 797        
 798        mov dptr, IN4CS
 799        movx a, @dptr
 800        jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
 801        mov dptr, IN4BUF
 802        
 803        mov a, tx_ring_in
 804        movx @dptr, a
 805        inc dptr
 806        mov a, tx_ring_out
 807        movx @dptr, a
 808        inc dptr
 809
 810        mov a, rx_ring_in
 811        movx @dptr, a
 812        inc dptr
 813        mov a, rx_ring_out
 814        movx @dptr, a
 815        inc dptr
 816        
 817        clr a
 818        jnb TX_RUNNING, dump_stat__no_tx_running
 819        inc a
 820dump_stat__no_tx_running:
 821        movx @dptr, a
 822        inc dptr
 823        ;; tx_ring[0..15]
 824        inc dps
 825        mov dptr, #tx_ring      ; DPTR1: source
 826        mov r1, #16
 827dump_stat__tx_ring_loop:
 828        movx a, @dptr
 829        inc dptr
 830        inc dps
 831        movx @dptr, a
 832        inc dptr
 833        inc dps
 834        djnz r1, dump_stat__tx_ring_loop
 835        inc dps
 836        
 837        mov a, #0xfc
 838        movx @dptr, a
 839        inc dptr
 840        
 841        ;; rx_ring[0..15]
 842        inc dps
 843        mov dptr, #rx_ring      ; DPTR1: source
 844        mov r1, #16
 845dump_stat__rx_ring_loop:
 846        movx a, @dptr
 847        inc dptr
 848        inc dps
 849        movx @dptr, a
 850        inc dptr
 851        inc dps
 852        djnz r1, dump_stat__rx_ring_loop
 853        
 854        ;; now send it
 855        clr a
 856        mov dps, a
 857        mov dptr, IN4BC
 858        mov a, #38
 859        movx @dptr, a
 860dump_stat__done:        
 861        ret
 862                
 863;;; ============================================================
 864        
 865maybe_start_tx:
 866        ;; make sure the tx process is running.
 867        jb TX_RUNNING, start_tx_done
 868start_tx:
 869        ;; is there work to be done?
 870        mov a, tx_ring_in
 871        cjne a,tx_ring_out, start_tx__work
 872        ret                     ; no work
 873start_tx__work: 
 874        ;; tx was not running. send the first character, setup the TI int
 875        inc tx_ring_out         ; [++tx_ring_out]
 876        mov dph, #HIGH(tx_ring)
 877        mov dpl, tx_ring_out
 878        movx a, @dptr
 879        mov sbuf, a
 880        setb TX_RUNNING
 881start_tx_done:
 882        ;; can we unthrottle the host tx process?
 883        ;;  step 1: do we care?
 884        mov a, #0
 885        cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
 886        ;; nope
 887start_tx_really_done:
 888        ret
 889start_tx__maybe_unthrottle_tx:
 890        ;;  step 2: is there now room?
 891        mov a, tx_ring_out
 892        setb c
 893        subb a, tx_ring_in
 894        ;; a is now write_room. If thresh >= a, we can unthrottle
 895        clr c
 896        subb a, tx_unthrottle_threshold
 897        jc start_tx_really_done ; nope
 898        ;; yes, we can unthrottle. remove the threshold and mark a request
 899        mov tx_unthrottle_threshold, #0
 900        setb DO_TX_UNTHROTTLE
 901        ;; prod rx, which will actually send the message when in2 becomes free
 902        ljmp start_in
 903        
 904
 905serial_int:
 906        push dps
 907        push dpl
 908        push dph
 909        push dpl1
 910        push dph1
 911        push acc
 912        jnb TI, serial_int__not_tx
 913        ;; tx finished. send another character if we have one
 914        clr TI                  ; clear int
 915        clr TX_RUNNING
 916        lcall start_tx
 917serial_int__not_tx:
 918        jnb RI, serial_int__not_rx
 919        lcall get_rx_char
 920        clr RI                  ; clear int
 921serial_int__not_rx:     
 922        ;; return
 923        pop acc
 924        pop dph1
 925        pop dpl1
 926        pop dph
 927        pop dpl
 928        pop dps
 929        reti
 930
 931get_rx_char:
 932        mov dph, #HIGH(rx_ring)
 933        mov dpl, rx_ring_in
 934        inc dpl                 ; target = rx_ring_in+1
 935        mov a, sbuf
 936        movx @dptr, a
 937        ;; check for overflow before incrementing rx_ring_in
 938        mov a, dpl
 939        cjne a, rx_ring_out, get_rx_char__no_overflow
 940        ;; signal overflow
 941        ret
 942get_rx_char__no_overflow:       
 943        inc rx_ring_in
 944        ;; kick off USB INpipe
 945        acall start_in
 946        ret
 947
 948start_in:
 949        ;; check if the inpipe is already running.
 950        mov dptr, IN2CS
 951        movx a, @dptr
 952        jb acc.1, start_in__done; int will handle it
 953        jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
 954        ;; see if there is any work to do. a serial interrupt might occur
 955        ;; during this sequence?
 956        mov a, rx_ring_in
 957        cjne a, rx_ring_out, start_in__have_work
 958        ret                     ; nope
 959start_in__have_work:    
 960        ;; now copy as much data as possible into the pipe. 63 bytes max.
 961        clr a
 962        mov dps, a
 963        mov dph, #HIGH(rx_ring) ; load DPTR0 with source
 964        inc dps
 965        mov dptr, IN2BUF        ; load DPTR1 with target
 966        movx @dptr, a           ; in[0] signals that rest of IN is rx data
 967        inc dptr
 968        inc dps
 969        ;; loop until we run out of data, or we have copied 64 bytes
 970        mov r1, #1              ; INbuf size counter
 971start_in__loop:
 972        mov a, rx_ring_in
 973        cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
 974        sjmp start_in__kick
 975start_inlocal_irq_enablell_copying:
 976        inc rx_ring_out
 977        mov dpl, rx_ring_out
 978        movx a, @dptr
 979        inc dps
 980        movx @dptr, a           ; write into IN buffer
 981        inc dptr
 982        inc dps
 983        inc r1
 984        cjne r1, #64, start_in__loop; loop
 985start_in__kick:
 986        ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
 987        ;; kick off IN
 988        mov dptr, IN2BC
 989        mov a, r1
 990        jz start_in__done
 991        movx @dptr, a
 992        ;; done
 993start_in__done:
 994        ;acall dump_stat
 995        ret
 996start_in__do_tx_unthrottle:
 997        ;; special sequence: send a tx unthrottle message
 998        clr DO_TX_UNTHROTTLE
 999        clr a
1000        mov dps, a
1001        mov dptr, IN2BUF
1002        mov a, #1
1003        movx @dptr, a
1004        inc dptr
1005        mov a, #2
1006        movx @dptr, a
1007        mov dptr, IN2BC
1008        movx @dptr, a
1009        ret
1010        
1011putchar:
1012        clr TI
1013        mov SBUF, a
1014putchar_wait:
1015        jnb TI, putchar_wait
1016        clr TI
1017        ret
1018
1019        
1020baud_table:                     ; baud_high, then baud_low
1021        ;; baud[0]: 110
1022        .byte BAUD_HIGH(110)
1023        .byte BAUD_LOW(110)
1024        ;; baud[1]: 300
1025        .byte BAUD_HIGH(300)
1026        .byte BAUD_LOW(300)
1027        ;; baud[2]: 1200
1028        .byte BAUD_HIGH(1200)
1029        .byte BAUD_LOW(1200)
1030        ;; baud[3]: 2400
1031        .byte BAUD_HIGH(2400)
1032        .byte BAUD_LOW(2400)
1033        ;; baud[4]: 4800
1034        .byte BAUD_HIGH(4800)
1035        .byte BAUD_LOW(4800)
1036        ;; baud[5]: 9600
1037        .byte BAUD_HIGH(9600)
1038        .byte BAUD_LOW(9600)
1039        ;; baud[6]: 19200
1040        .byte BAUD_HIGH(19200)
1041        .byte BAUD_LOW(19200)
1042        ;; baud[7]: 38400
1043        .byte BAUD_HIGH(38400)
1044        .byte BAUD_LOW(38400)
1045        ;; baud[8]: 57600
1046        .byte BAUD_HIGH(57600)
1047        .byte BAUD_LOW(57600)
1048        ;; baud[9]: 115200
1049        .byte BAUD_HIGH(115200)
1050        .byte BAUD_LOW(115200)
1051
1052desc_device:
1053        .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
1054        .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
1055;;; The "real" device id, which must match the host driver, is that
1056;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1057        
1058desc_config1:
1059        .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1060        .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1061        .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1062        .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1063
1064desc_strings:
1065        .word string_langids, string_mfg, string_product, string_serial
1066desc_strings_end:
1067
1068string_langids: .byte string_langids_end-string_langids
1069        .byte 3
1070        .word 0
1071string_langids_end:
1072
1073        ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
1074        ;; *that* is a pain in the ass to encode. And they are little-endian
1075        ;; too. Use this perl snippet to get the bytecodes:
1076        /* while (<>) {
1077            @c = split(//);
1078            foreach $c (@c) {
1079             printf("0x%02x, 0x00, ", ord($c));
1080            }
1081           }
1082        */
1083
1084string_mfg:     .byte string_mfg_end-string_mfg
1085        .byte 3
1086;       .byte "ACME usb widgets"
1087        .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
1088string_mfg_end:
1089        
1090string_product: .byte string_product_end-string_product
1091        .byte 3
1092;       .byte "ACME USB serial widget"
1093        .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
1094string_product_end:
1095        
1096string_serial:  .byte string_serial_end-string_serial
1097        .byte 3
1098;       .byte "47"
1099        .byte 0x34, 0x00, 0x37, 0x00
1100string_serial_end:
1101                
1102;;; ring buffer memory
1103        ;; tx_ring_in+1 is where the next input byte will go
1104        ;; [tx_ring_out] has been sent
1105        ;; if tx_ring_in == tx_ring_out, theres no work to do
1106        ;; there are (tx_ring_in - tx_ring_out) chars to be written
1107        ;; dont let _in lap _out
1108        ;;   cannot inc if tx_ring_in+1 == tx_ring_out
1109        ;;  write [tx_ring_in+1] then tx_ring_in++
1110        ;;   if (tx_ring_in+1 == tx_ring_out), overflow
1111        ;;   else tx_ring_in++
1112        ;;  read/send [tx_ring_out+1], then tx_ring_out++
1113
1114        ;; rx_ring_in works the same way
1115        
1116        .org 0x1000
1117tx_ring:
1118        .skip 0x100             ; 256 bytes
1119rx_ring:
1120        .skip 0x100             ; 256 bytes
1121        
1122        
1123        .END
1124        
1125