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.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 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