Linux Audio

Check our new training course

Loading...
v4.10.11
   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	
v3.1
   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