linux/firmware/keyspan_pda/xircom_pgs.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 PORTBCFG #0x7f94
 154#define PORTCCFG #0x7f95
 155#define OEA     #0x7f9c
 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
 167                
 168        ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
 169
 170        .org 0
 171        ljmp start
 172        ;; interrupt vectors
 173        .org 23H
 174        ljmp serial_int
 175        .byte 0
 176        
 177        .org 43H
 178        ljmp USB_Jump_Table
 179        .byte 0                 ; filled in by the USB core
 180
 181;;; local variables. These are not initialized properly: do it by hand.
 182        .org 30H
 183rx_ring_in:     .byte 0
 184rx_ring_out:    .byte 0
 185tx_ring_in:     .byte 0
 186tx_ring_out:    .byte 0
 187tx_unthrottle_threshold:        .byte 0
 188                
 189        .org 0x100H             ; wants to be on a page boundary
 190USB_Jump_Table:
 191        ljmp    ISR_Sudav       ; Setup Data Available
 192        .byte 0
 193        ljmp    0               ; Start of Frame
 194        .byte 0
 195        ljmp    0               ; Setup Data Loading
 196        .byte 0
 197        ljmp    0               ; Global Suspend
 198        .byte   0
 199        ljmp    0               ; USB Reset     
 200        .byte   0
 201        ljmp    0               ; Reserved
 202        .byte   0
 203        ljmp    0               ; End Point 0 In
 204        .byte   0
 205        ljmp    0               ; End Point 0 Out
 206        .byte   0
 207        ljmp    0               ; End Point 1 In
 208        .byte   0
 209        ljmp    0               ; End Point 1 Out
 210        .byte   0
 211        ljmp    ISR_Ep2in
 212        .byte   0
 213        ljmp    ISR_Ep2out
 214        .byte   0
 215
 216
 217        .org 0x200
 218                
 219start:  mov SP,STACK-1 ; set stack
 220        ;; clear local variables
 221        clr a
 222        mov tx_ring_in, a
 223        mov tx_ring_out, a
 224        mov rx_ring_in, a
 225        mov rx_ring_out, a
 226        mov tx_unthrottle_threshold, a
 227        clr TX_RUNNING
 228        clr DO_TX_UNTHROTTLE
 229        
 230        ;; clear fifo with "fe"
 231        mov r1, 0
 232        mov a, #0xfe
 233        mov dptr, #tx_ring
 234clear_tx_ring_loop:
 235        movx @dptr, a
 236        inc dptr
 237        djnz r1, clear_tx_ring_loop
 238
 239        mov a, #0xfd
 240        mov dptr, #rx_ring
 241clear_rx_ring_loop:
 242        movx @dptr, a
 243        inc dptr
 244        djnz r1, clear_rx_ring_loop
 245
 246;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
 247;;; on Xircom the STANDBY is wired to PB6 and PC4 
 248        mov dptr, PORTBCFG
 249        mov a, #0xBf
 250        movx @dptr, a
 251        mov dptr, PORTCCFG
 252        mov a, #0xef
 253        movx @dptr, a
 254        
 255        ;; set OEC.4
 256        mov a, #0x10
 257        mov dptr,OEC
 258        movx @dptr,a
 259
 260        ;; clear PC4
 261        mov a, #0x00
 262        mov dptr,OUTC
 263        movx @dptr,a
 264
 265        ;; set OEB.6
 266        mov a, #0x40
 267        mov dptr,OEB
 268        movx @dptr,a
 269
 270        ;; clear PB6
 271        mov a, #0x00
 272        mov dptr,OUTB
 273        movx @dptr,a
 274
 275        ;; set OEC.[17]
 276        mov a, #0x82
 277        mov dptr,OEC
 278        movx @dptr,a
 279
 280
 281        ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
 282        mov dptr, PORTCCFG
 283        mov a, #0x03
 284        movx @dptr, a
 285        
 286        ;; set up interrupts, autovectoring
 287        ;; set BKPT
 288        mov dptr, USBBAV
 289        movx a,@dptr
 290        setb acc.0              ; AVEN bit to 0
 291        movx @dptr, a
 292
 293        mov a,#0x01             ; enable SUDAV: setup data available (for ep0)
 294        mov dptr, USBIRQ
 295        movx @dptr, a           ; clear SUDAVI
 296        mov dptr, USBIEN
 297        movx @dptr, a
 298        
 299        mov dptr, IN07IEN
 300        mov a,#0x04             ; enable IN2 int
 301        movx @dptr, a
 302        
 303        mov dptr, OUT07IEN
 304        mov a,#0x04             ; enable OUT2 int
 305        movx @dptr, a
 306        mov dptr, OUT2BC
 307        movx @dptr, a           ; arm OUT2
 308
 309;;      mov a, #0x84            ; turn on RTS, DTR
 310;;      mov dptr,OUTC
 311;;      movx @dptr, a
 312
 313        mov a, #0x7             ; turn on  DTR
 314        mov dptr,USBBAV
 315        movx @dptr, a
 316
 317        mov a, #0x20             ; turn on the RED led 
 318        mov dptr,OEA
 319        movx @dptr, a
 320
 321        mov a, #0x80            ; turn on  RTS
 322        mov dptr,OUTC
 323        movx @dptr, a
 324
 325        ;; setup the serial port. 9600 8N1.
 326        mov a,#0x53             ; mode 1, enable rx, clear int
 327        mov SCON, a
 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
 333        ;;    300: 0xF63C
 334#define BAUD 9600
 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))
 338                
 339        mov T2CON, #030h        ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
 340        mov r3, #5
 341        acall set_baud
 342        setb TR2
 343        mov SCON, #050h
 344        
 345#if 0
 346        mov r1, #0x40
 347        mov a, #0x41
 348send:   
 349        mov SBUF, a
 350        inc a
 351        anl a, #0x3F
 352        orl a, #0x40
 353;       xrl a, #0x02
 354wait1:  
 355        jnb TI, wait1
 356        clr TI
 357        djnz r1, send
 358;done:  sjmp done
 359
 360#endif
 361        
 362        setb EUSB
 363        setb EA
 364        setb ES0
 365        ;acall dump_stat
 366
 367        ;; hey, what say we RENUMERATE! (TRM p.62)
 368        mov a, #0
 369        mov dps, a
 370        mov dptr, USBCS
 371        mov a, #0x02            ; DISCON=0, DISCOE=0, RENUM=1
 372        movx @dptr, a
 373        ;; now presence pin is floating, simulating disconnect. wait 0.5s
 374        mov r1, #46
 375renum_wait1:
 376        mov r2, #0
 377renum_wait2:
 378        mov r3, #0
 379renum_wait3:
 380        djnz r3, renum_wait3
 381        djnz r2, renum_wait2
 382        djnz r1, renum_wait1    ; wait about n*(256^2) 6MHz clocks
 383        mov a, #0x06            ; DISCON=0, DISCOE=1, RENUM=1
 384        movx @dptr, a
 385        ;; we are back online. the host device will now re-query us
 386        
 387        
 388main:   sjmp main
 389
 390        
 391
 392ISR_Sudav:
 393        push dps
 394        push dpl
 395        push dph
 396        push dpl1
 397        push dph1
 398        push acc
 399        mov a,EXIF
 400        clr acc.4
 401        mov EXIF,a              ; clear INT2 first
 402        mov dptr, USBIRQ        ; clear USB int
 403        mov a,#01h
 404        movx @dptr,a
 405
 406        ;; get request type
 407        mov dptr, SETUPDAT
 408        movx a, @dptr
 409        mov r1, a               ; r1 = bmRequestType
 410        inc dptr
 411        movx a, @dptr
 412        mov r2, a               ; r2 = bRequest
 413        inc dptr
 414        movx a, @dptr
 415        mov r3, a               ; r3 = wValueL
 416        inc dptr
 417        movx a, @dptr
 418        mov r4, a               ; r4 = wValueH
 419
 420        ;; main switch on bmRequest.type: standard or vendor
 421        mov a, r1
 422        anl a, #0x60
 423        cjne a, #0x00, setup_bmreq_type_not_standard
 424        ;; standard request: now main switch is on bRequest
 425        ljmp setup_bmreq_is_standard
 426        
 427setup_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
 436        ljmp setup_done_ack
 437setup_bmreq_type_not_standard__do_stall:
 438        ljmp setup_stall
 439setup_ctrl_not_00:
 440        cjne r2, #0x01, setup_ctrl_not_01
 441        ;; 01 is reserved for set bits (parity). TODO
 442        ljmp setup_stall
 443setup_ctrl_not_01:
 444        cjne r2, #0x02, setup_ctrl_not_02
 445        ;; 02 is set HW flow control. TODO
 446        ljmp setup_stall
 447setup_ctrl_not_02:
 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
 452setup_ctrl_not_03:
 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
 457        mov dptr, PORTCCFG
 458        movx a, @dptr
 459        orl a, #0x02
 460        movx @dptr, a
 461        ljmp setup_done_ack
 462setup_ctrl_do_break_on:
 463        ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
 464        mov dptr, OUTC
 465        movx a, @dptr
 466        anl a, #0xfd            ; ~0x02
 467        movx @dptr, a
 468        mov dptr, PORTCCFG
 469        movx a, @dptr
 470        anl a, #0xfd            ; ~0x02
 471        movx @dptr, a
 472        ljmp setup_done_ack
 473setup_ctrl_not_04:
 474        cjne r2, #0x05, setup_ctrl_not_05
 475        ;; 05 is set desired interrupt bitmap. TODO
 476        ljmp setup_stall
 477setup_ctrl_not_05:
 478        cjne r2, #0x06, setup_ctrl_not_06
 479        ;; 06 is query room
 480        cjne r3, #0x00, setup_ctrl_06_not_00
 481        ;; 06, wValue[0]=0 is query write_room
 482        mov a, tx_ring_out
 483        setb c
 484        subb a, tx_ring_in      ; out-1-in = 255 - (in-out)
 485        ljmp setup_return_one_byte
 486setup_ctrl_06_not_00:
 487        cjne r3, #0x01, setup_ctrl_06_not_01
 488        ;; 06, wValue[0]=1 is query chars_in_buffer
 489        mov a, tx_ring_in
 490        clr c
 491        subb a, tx_ring_out     ; in-out
 492        ljmp setup_return_one_byte
 493setup_ctrl_06_not_01:   
 494        ljmp setup_stall
 495setup_ctrl_not_06:
 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
 499        ljmp setup_done_ack
 500setup_ctrl_not_07:
 501        ljmp setup_stall
 502        
 503setup_bmreq_type_not_vendor:
 504        ljmp setup_stall
 505
 506
 507setup_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
 514setup_return_two_zero_bytes:
 515        mov dptr, IN0BUF
 516        clr a
 517        movx @dptr, a
 518        inc dptr
 519        movx @dptr, a
 520        mov dptr, IN0BC
 521        mov a, #2
 522        movx @dptr, a
 523        ljmp setup_done_ack
 524setup_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
 530setup_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
 534setup_Get_Status_not_interface: 
 535        ljmp setup_stall
 536        
 537setup_breq_not_00:
 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
 542        ljmp setup_stall
 543setup_Clear_Feature_not_stall:
 544        cjne r3, #0x01, setup_Clear_Feature_not_rwake
 545        ;; Clear_Feature(remote wakeup). ignored.
 546        ljmp setup_done_ack
 547setup_Clear_Feature_not_rwake:
 548        ljmp setup_stall
 549        
 550setup_breq_not_01:
 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
 555        ljmp setup_stall
 556setup_Set_Feature_not_stall:
 557        cjne r3, #0x01, setup_Set_Feature_not_rwake
 558        ;; Set_Feature(remote wakeup). ignored.
 559        ljmp setup_done_ack
 560setup_Set_Feature_not_rwake:
 561        ljmp setup_stall
 562        
 563setup_breq_not_03:      
 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)
 568        mov dptr, SUDPTRH
 569        mov a, #HIGH(desc_device)
 570        movx @dptr, a
 571        mov dptr, SUDPTRL
 572        mov a, #LOW(desc_device)
 573        movx @dptr, a
 574        ljmp setup_done_ack
 575setup_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])
 580        mov dptr, SUDPTRH
 581        mov a, #HIGH(desc_config1)
 582        movx @dptr, a
 583        mov dptr, SUDPTRL
 584        mov a, #LOW(desc_config1)
 585        movx @dptr, a
 586        ljmp setup_done_ack
 587setup_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)
 592        clr c
 593        subb a,r3               ; a=4, r3 = 0..3 . if a<=0 then stall
 594        jc  setup_stall
 595        jz  setup_stall
 596        mov a, r3
 597        add a, r3               ; a = 2*wValueL
 598        mov dptr, #desc_strings
 599        add a, dpl
 600        mov dpl, a
 601        mov a, #0
 602        addc a, dph
 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.
 615        movx a, @dptr
 616        mov r1, a
 617        inc dptr
 618        movx a, @dptr
 619        mov r2, a
 620        mov dptr, SUDPTRH
 621        mov a, r1
 622        movx @dptr, a
 623        mov dptr, SUDPTRL
 624        mov a, r2
 625        movx @dptr, a
 626        ;; done
 627        ljmp setup_done_ack
 628        
 629setup_Get_Descriptor_not_string:
 630        ljmp setup_stall
 631        
 632setup_breq_not_06:
 633        cjne r2, #0x08, setup_breq_not_08
 634        ;; Get_Configuration. always 1. return one byte.
 635        ;; this is reusable
 636        mov a, #1
 637setup_return_one_byte:  
 638        mov dptr, IN0BUF
 639        movx @dptr, a
 640        mov a, #1
 641        mov dptr, IN0BC
 642        movx @dptr, a
 643        ljmp setup_done_ack
 644setup_breq_not_08:
 645        cjne r2, #0x09, setup_breq_not_09
 646        ;; 09: Set_Configuration. ignored.
 647        ljmp setup_done_ack
 648setup_breq_not_09:
 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
 652        mov a, #0
 653        ljmp setup_return_one_byte
 654setup_breq_not_0a:
 655        cjne r2, #0x0b, setup_breq_not_0b
 656        ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
 657        ljmp setup_done_ack
 658setup_breq_not_0b:
 659        ljmp setup_stall
 660
 661                
 662setup_done_ack: 
 663        ;; now clear HSNAK
 664        mov dptr, EP0CS
 665        mov a, #0x02
 666        movx @dptr, a
 667        sjmp setup_done
 668setup_stall:    
 669        ;; unhandled. STALL
 670        ;EP0CS |= bmEPSTALL
 671        mov dptr, EP0CS
 672        movx a, @dptr
 673        orl a, EP0STALLbit
 674        movx @dptr, a
 675        sjmp setup_done
 676        
 677setup_done:     
 678        pop acc
 679        pop dph1
 680        pop dpl1
 681        pop dph
 682        pop dpl
 683        pop dps
 684        reti
 685
 686;;; ==============================================================
 687        
 688set_baud:                       ; baud index in r3
 689        ;; verify a < 10
 690        mov a, r3
 691        jb ACC.7, set_baud__badbaud
 692        clr c
 693        subb a, #10
 694        jnc set_baud__badbaud
 695        mov a, r3
 696        rl a                    ; a = index*2
 697        add a, #LOW(baud_table)
 698        mov dpl, a
 699        mov a, #HIGH(baud_table)
 700        addc a, #0
 701        mov dph, a
 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
 706        mov RCAP2H, a
 707        mov TH2, a
 708        inc dptr
 709        movx a,@dptr            ; BAUD_LOW
 710        mov RCAP2L, a
 711        mov TL2, a
 712        ;; TODO: restart xmit/receive
 713        ;; TODO: reenable interrupts, resume tx if pending
 714        clr c                   ; c=0: success
 715        ret
 716set_baud__badbaud:
 717        setb c                  ; c=1: failure
 718        ret
 719        
 720;;; ==================================================
 721control_pins:
 722        cjne r1, #0x41, control_pins_in
 723control_pins_out:
 724                ;TODO BKPT is DTR
 725        mov a, r3 ; wValue[0] holds new bits:   b7 is new RTS
 726        xrl a, #0xff            ; 1 means active, 0V, +12V ?
 727        anl a, #0x80
 728        mov r3, a
 729        mov dptr, OUTC
 730        movx a, @dptr           ; only change bit 7 
 731        anl a, #0x7F            ; ~0x84
 732        orl a, r3
 733        movx @dptr, a           ; other pins are inputs, bits ignored
 734        ljmp setup_done_ack
 735control_pins_in:
 736        mov dptr, PINSC
 737        movx a, @dptr
 738        xrl a, #0xff
 739        ljmp setup_return_one_byte
 740
 741;;; ========================================
 742        
 743ISR_Ep2in:
 744        push dps
 745        push dpl
 746        push dph
 747        push dpl1
 748        push dph1
 749        push acc
 750        mov a,EXIF
 751        clr acc.4
 752        mov EXIF,a              ; clear INT2 first
 753        mov dptr, IN07IRQ       ; clear USB int
 754        mov a,#04h
 755        movx @dptr,a
 756
 757        mov a, #0x20             ; Turn off the green LED
 758        mov dptr,OEA
 759        movx @dptr, a
 760
 761
 762        ;; do stuff
 763        lcall start_in
 764
 765        mov a, #0x20             ; Turn off the green LED
 766        mov dptr,OEA
 767        movx @dptr, a
 768
 769
 770        
 771        pop acc
 772        pop dph1
 773        pop dpl1
 774        pop dph
 775        pop dpl
 776        pop dps
 777        reti
 778
 779ISR_Ep2out:
 780        push dps
 781        push dpl
 782        push dph
 783        push dpl1
 784        push dph1
 785        push acc
 786
 787        mov a, #0x10             ; Turn the green LED
 788        mov dptr,OEA
 789        movx @dptr, a
 790
 791
 792
 793        mov a,EXIF
 794        clr acc.4
 795        mov EXIF,a              ; clear INT2 first
 796        mov dptr, OUT07IRQ      ; clear USB int
 797        mov a,#04h
 798        movx @dptr,a
 799
 800        ;; do stuff
 801
 802        ;; copy data into buffer. for now, assume we will have enough space
 803        mov dptr, OUT2BC        ; get byte count
 804        movx a,@dptr
 805        mov r1, a
 806        clr a
 807        mov dps, a
 808        mov dptr, OUT2BUF       ; load DPTR0 with source
 809        mov dph1, #HIGH(tx_ring)        ; load DPTR1 with target
 810        mov dpl1, tx_ring_in
 811OUT_loop:
 812        movx a,@dptr            ; read
 813        inc dps                 ; switch to DPTR1: target
 814        inc dpl1                ; target = tx_ring_in+1
 815        movx @dptr,a            ; store
 816        mov a,dpl1
 817        cjne a, tx_ring_out, OUT_no_overflow
 818        sjmp OUT_overflow
 819OUT_no_overflow:        
 820        inc tx_ring_in          ; tx_ring_in++
 821        inc dps                 ; switch to DPTR0: source
 822        inc dptr
 823        djnz r1, OUT_loop
 824        sjmp OUT_done
 825OUT_overflow:
 826        ;; signal overflow
 827        ;; fall through
 828OUT_done:       
 829        ;; ack
 830        mov dptr,OUT2BC
 831        movx @dptr,a
 832
 833        ;; start tx
 834        acall maybe_start_tx
 835        ;acall dump_stat
 836
 837        mov a, #0x20             ; Turn off the green LED
 838        mov dptr,OEA
 839        movx @dptr, a
 840        
 841        pop acc
 842        pop dph1
 843        pop dpl1
 844        pop dph
 845        pop dpl
 846        pop dps
 847        reti
 848
 849dump_stat:
 850        ;; fill in EP4in with a debugging message:
 851        ;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
 852        ;;   tx_active
 853        ;;   tx_ring[0..15]
 854        ;;   0xfc
 855        ;;   rx_ring[0..15]
 856        clr a
 857        mov dps, a
 858        
 859        mov dptr, IN4CS
 860        movx a, @dptr
 861        jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
 862        mov dptr, IN4BUF
 863        
 864        mov a, tx_ring_in
 865        movx @dptr, a
 866        inc dptr
 867        mov a, tx_ring_out
 868        movx @dptr, a
 869        inc dptr
 870
 871        mov a, rx_ring_in
 872        movx @dptr, a
 873        inc dptr
 874        mov a, rx_ring_out
 875        movx @dptr, a
 876        inc dptr
 877        
 878        clr a
 879        jnb TX_RUNNING, dump_stat__no_tx_running
 880        inc a
 881dump_stat__no_tx_running:
 882        movx @dptr, a
 883        inc dptr
 884        ;; tx_ring[0..15]
 885        inc dps
 886        mov dptr, #tx_ring      ; DPTR1: source
 887        mov r1, #16
 888dump_stat__tx_ring_loop:
 889        movx a, @dptr
 890        inc dptr
 891        inc dps
 892        movx @dptr, a
 893        inc dptr
 894        inc dps
 895        djnz r1, dump_stat__tx_ring_loop
 896        inc dps
 897        
 898        mov a, #0xfc
 899        movx @dptr, a
 900        inc dptr
 901        
 902        ;; rx_ring[0..15]
 903        inc dps
 904        mov dptr, #rx_ring      ; DPTR1: source
 905        mov r1, #16
 906dump_stat__rx_ring_loop:
 907        movx a, @dptr
 908        inc dptr
 909        inc dps
 910        movx @dptr, a
 911        inc dptr
 912        inc dps
 913        djnz r1, dump_stat__rx_ring_loop
 914        
 915        ;; now send it
 916        clr a
 917        mov dps, a
 918        mov dptr, IN4BC
 919        mov a, #38
 920        movx @dptr, a
 921dump_stat__done:        
 922        ret
 923                
 924;;; ============================================================
 925        
 926maybe_start_tx:
 927        ;; make sure the tx process is running.
 928        jb TX_RUNNING, start_tx_done
 929start_tx:
 930        ;; is there work to be done?
 931        mov a, tx_ring_in
 932        cjne a,tx_ring_out, start_tx__work
 933        ret                     ; no work
 934start_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)
 938        mov dpl, tx_ring_out
 939        movx a, @dptr
 940        mov sbuf, a
 941        setb TX_RUNNING
 942start_tx_done:
 943        ;; can we unthrottle the host tx process?
 944        ;;  step 1: do we care?
 945        mov a, #0
 946        cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
 947        ;; nope
 948start_tx_really_done:
 949        ret
 950start_tx__maybe_unthrottle_tx:
 951        ;;  step 2: is there now room?
 952        mov a, tx_ring_out
 953        setb c
 954        subb a, tx_ring_in
 955        ;; a is now write_room. If thresh >= a, we can unthrottle
 956        clr c
 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
 963        ljmp start_in
 964        
 965
 966serial_int:
 967        push dps
 968        push dpl
 969        push dph
 970        push dpl1
 971        push dph1
 972        push acc
 973        jnb TI, serial_int__not_tx
 974        ;; tx finished. send another character if we have one
 975        clr TI                  ; clear int
 976        clr TX_RUNNING
 977        lcall start_tx
 978serial_int__not_tx:
 979        jnb RI, serial_int__not_rx
 980        lcall get_rx_char
 981        clr RI                  ; clear int
 982serial_int__not_rx:     
 983        ;; return
 984        pop acc
 985        pop dph1
 986        pop dpl1
 987        pop dph
 988        pop dpl
 989        pop dps
 990        reti
 991
 992get_rx_char:
 993        mov dph, #HIGH(rx_ring)
 994        mov dpl, rx_ring_in
 995        inc dpl                 ; target = rx_ring_in+1
 996        mov a, sbuf
 997        movx @dptr, a
 998        ;; check for overflow before incrementing rx_ring_in
 999        mov a, dpl
1000        cjne a, rx_ring_out, get_rx_char__no_overflow
1001        ;; signal overflow
1002        ret
1003get_rx_char__no_overflow:       
1004        inc rx_ring_in
1005        ;; kick off USB INpipe
1006        acall start_in
1007        ret
1008
1009start_in:
1010        ;; check if the inpipe is already running.
1011        mov  a,#0x10
1012        mov dptr, OEA
1013        movx @dptr,a
1014
1015        mov dptr, IN2CS
1016        movx a, @dptr
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?
1021        mov a, rx_ring_in
1022        cjne a, rx_ring_out, start_in__have_work
1023        ret                     ; nope
1024start_in__have_work:    
1025        ;; now copy as much data as possible into the pipe. 63 bytes max.
1026        clr a
1027        mov dps, a
1028        mov dph, #HIGH(rx_ring) ; load DPTR0 with source
1029        inc dps
1030        mov dptr, IN2BUF        ; load DPTR1 with target
1031        movx @dptr, a           ; in[0] signals that rest of IN is rx data
1032        inc dptr
1033        inc dps
1034        ;; loop until we run out of data, or we have copied 64 bytes
1035        mov r1, #1              ; INbuf size counter
1036start_in__loop:
1037        mov a, rx_ring_in
1038        cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
1039        sjmp start_in__kick
1040start_inlocal_irq_enablell_copying:
1041        inc rx_ring_out
1042        mov dpl, rx_ring_out
1043        movx a, @dptr
1044        inc dps
1045        movx @dptr, a           ; write into IN buffer
1046        inc dptr
1047        inc dps
1048        inc r1
1049        cjne r1, #64, start_in__loop; loop
1050start_in__kick:
1051        ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
1052        ;; kick off IN
1053        mov a, #0x10             ; Turn the green LED
1054        mov dptr,OEA
1055        movx @dptr, a
1056        mov dptr, IN2BC
1057        mov a, r1
1058        jz start_in__done
1059        movx @dptr, a
1060        ;; done
1061start_in__done:
1062        ;acall dump_stat
1063        ret
1064start_in__do_tx_unthrottle:
1065        ;; special sequence: send a tx unthrottle message
1066        clr DO_TX_UNTHROTTLE
1067        clr a
1068        mov dps, a
1069        mov dptr, IN2BUF
1070        mov a, #1
1071        movx @dptr, a
1072        inc dptr
1073        mov a, #2
1074        movx @dptr, a
1075        mov dptr, IN2BC
1076        movx @dptr, a
1077        ret
1078        
1079putchar:
1080        clr TI
1081        mov SBUF, a
1082putchar_wait:
1083        jnb TI, putchar_wait
1084        clr TI
1085        ret
1086
1087        
1088baud_table:                     ; baud_high, then baud_low
1089        ;; baud[0]: 110
1090        .byte BAUD_HIGH(110)
1091        .byte BAUD_LOW(110)
1092        ;; baud[1]: 300
1093        .byte BAUD_HIGH(300)
1094        .byte BAUD_LOW(300)
1095        ;; baud[2]: 1200
1096        .byte BAUD_HIGH(1200)
1097        .byte BAUD_LOW(1200)
1098        ;; baud[3]: 2400
1099        .byte BAUD_HIGH(2400)
1100        .byte BAUD_LOW(2400)
1101        ;; baud[4]: 4800
1102        .byte BAUD_HIGH(4800)
1103        .byte BAUD_LOW(4800)
1104        ;; baud[5]: 9600
1105        .byte BAUD_HIGH(9600)
1106        .byte BAUD_LOW(9600)
1107        ;; baud[6]: 19200
1108        .byte BAUD_HIGH(19200)
1109        .byte BAUD_LOW(19200)
1110        ;; baud[7]: 38400
1111        .byte BAUD_HIGH(38400)
1112        .byte BAUD_LOW(38400)
1113        ;; baud[8]: 57600
1114        .byte BAUD_HIGH(57600)
1115        .byte BAUD_LOW(57600)
1116        ;; baud[9]: 115200
1117        .byte BAUD_HIGH(115200)
1118        .byte BAUD_LOW(115200)
1119
1120desc_device:
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
1125        
1126desc_config1:
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
1131
1132desc_strings:
1133        .word string_langids, string_mfg, string_product, string_serial
1134desc_strings_end:
1135
1136string_langids: .byte string_langids_end-string_langids
1137        .byte 3
1138        .word 0
1139string_langids_end:
1140
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:
1144        /* while (<>) {
1145            @c = split(//);
1146            foreach $c (@c) {
1147             printf("0x%02x, 0x00, ", ord($c));
1148            }
1149           }
1150        */
1151
1152string_mfg:     .byte string_mfg_end-string_mfg
1153        .byte 3
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
1156string_mfg_end:
1157        
1158string_product: .byte string_product_end-string_product
1159        .byte 3
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
1162string_product_end:
1163        
1164string_serial:  .byte string_serial_end-string_serial
1165        .byte 3
1166;       .byte "47"
1167        .byte 0x34, 0x00, 0x37, 0x00
1168string_serial_end:
1169                
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++
1181
1182        ;; rx_ring_in works the same way
1183        
1184        .org 0x1000
1185tx_ring:
1186        .skip 0x100             ; 256 bytes
1187rx_ring:
1188        .skip 0x100             ; 256 bytes
1189        
1190        
1191        .END
1192        
1193