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