summaryrefslogtreecommitdiff
path: root/keyspan_pda/keyspan_pda.S
blob: 602bcce524973c96f43fd11245679cccd555680f (plain)
    1 /*  $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
    2  * 
    3  *  Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
    4  *  the EzUSB microcontroller.
    5  * 
    6  *  (C) Copyright 2000 Brian Warner <warner@lothar.com>
    7  * 
    8  * 	This program is free software; you can redistribute it and/or modify
    9  * 	it under the terms of the GNU General Public License as published by
   10  * 	the Free Software Foundation; either version 2 of the License, or
   11  * 	(at your option) any later version.
   12  * 
   13  *  "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
   14  *  company.
   15  * 
   16  *  This serial adapter is basically an EzUSB chip and an RS-232 line driver
   17  *  in a little widget that has a DB-9 on one end and a USB plug on the other.
   18  *  It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
   19  *  as a baud-rate generator. The wiring is:
   20  *   PC0/RxD0 <- rxd (DB9 pin 2)         PC4 <- dsr pin 6
   21  *   PC1/TxD0 -> txd pin 3               PC5 <- ri  pin 9
   22  *   PC2      -> rts pin 7               PC6 <- dcd pin 1
   23  *   PC3      <- cts pin 8               PC7 -> dtr pin 4
   24  *   PB1 -> line driver standby
   25  *
   26  *  The EzUSB register constants below come from their excellent documentation
   27  *  and sample code (which used to be available at www.anchorchips.com, but
   28  *  that has now been absorbed into Cypress' site and the CD-ROM contents
   29  *  don't appear to be available online anymore). If we get multiple
   30  *  EzUSB-based drivers into the kernel, it might be useful to pull them out
   31  *  into a separate .h file.
   32  * 
   33  * THEORY OF OPERATION:
   34  *
   35  *   There are two 256-byte ring buffers, one for tx, one for rx.
   36  *
   37  *   EP2out is pure tx data. When it appears, the data is copied into the tx
   38  *   ring and serial transmission is started if it wasn't already running. The
   39  *   "tx buffer empty" interrupt may kick off another character if the ring
   40  *   still has data. If the host is tx-blocked because the ring filled up,
   41  *   it will request a "tx unthrottle" interrupt. If sending a serial character
   42  *   empties the ring below the desired threshold, we set a bit that will send
   43  *   up the tx unthrottle message as soon as the rx buffer becomes free.
   44  *
   45  *   EP2in (interrupt) is used to send both rx chars and rx status messages
   46  *   (only "tx unthrottle" at this time) back up to the host. The first byte
   47  *   of the rx message indicates data (0) or status msg (1). Status messages
   48  *   are sent before any data.
   49  *
   50  *   Incoming serial characters are put into the rx ring by the serial
   51  *   interrupt, and the EP2in buffer sent if it wasn't already in transit.
   52  *   When the EP2in buffer returns, the interrupt prompts us to send more
   53  *   rx chars (or status messages) if they are pending.
   54  *
   55  *   Device control happens through "vendor specific" control messages on EP0.
   56  *   All messages are destined for the "Interface" (with the index always 0,
   57  *   so that if their two-port device might someday use similar firmware, we
   58  *   can use index=1 to refer to the second port). The messages defined are:
   59  *
   60  *    bRequest = 0 : set baud/bits/parity
   61  *               1 : unused
   62  *               2 : reserved for setting HW flow control (CTSRTS)
   63  *               3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
   64  *               4 : set break (on/off)
   65  *               5 : reserved for requesting interrupts on pin state change
   66  *               6 : query buffer room or chars in tx buffer
   67  *               7 : request tx unthrottle interrupt
   68  *
   69  *  The host-side driver is set to recognize the device ID values stashed in
   70  *  serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
   71  *  start it running. This firmware will use EzUSB's "renumeration" trick by
   72  *  simulating a bus disconnect, then reconnect with a different device ID
   73  *  (encoded in the desc_device descriptor below). The host driver then
   74  *  recognizes the new device ID and glues it to the real serial driver code.
   75  *
   76  * USEFUL DOCS:
   77  *  EzUSB Technical Reference Manual: <http://www.anchorchips.com>
   78  *  8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
   79  *   basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
   80  *   use totally different registers!
   81  *  USB 1.1 spec: www.usb.org
   82  *
   83  * HOW TO BUILD:
   84  *  gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
   85  *  as31 -l keyspan_pda.asm
   86  *  mv keyspan_pda.obj keyspan_pda.hex
   87  *  perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
   88  * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
   89  * a bit to make it build.
   90  *
   91  * THANKS:
   92  *  Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
   93  *  AnchorChips, for making such an incredibly useful little microcontroller.
   94  *  KeySpan, for making a handy, cheap ($40) widget that was so easy to take
   95  *           apart and trace with an ohmmeter.
   96  *
   97  * TODO:
   98  *  lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
   99  *  control. Interrupting host upon change in DCD, etc, counting transitions.
  100  *  Need to find a safe device id to use (the one used by the Keyspan firmware
  101  *  under Windows would be ideal.. can anyone figure out what it is?). Parity.
  102  *  More baud rates. Oh, and the string-descriptor-length silicon bug
  103  *  workaround should be implemented, but I'm lazy, and the consequence is
  104  *  that the device name strings that show up in your kernel log will have
  105  *  lots of trailing binary garbage in them (appears as ????). Device strings
  106  *  should be made more accurate.
  107  *
  108  * Questions, bugs, patches to Brian.
  109  *
  110  *  -Brian Warner <warner@lothar.com>
  111  *
  112  */
  113 	
  114 #define HIGH(x) (((x) & 0xff00) / 256)
  115 #define LOW(x) ((x) & 0xff)
  116 
  117 #define dpl1 0x84
  118 #define dph1 0x85
  119 #define dps 0x86
  120 
  121 ;;; our bit assignments
  122 #define TX_RUNNING 0
  123 #define DO_TX_UNTHROTTLE 1
  124 	
  125 	;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
  126 #define STACK #0x60-1
  127 
  128 #define EXIF 0x91
  129 #define EIE 0xe8
  130 	.flag EUSB, EIE.0
  131 	.flag ES0, IE.4
  132 
  133 #define EP0CS #0x7fb4
  134 #define EP0STALLbit #0x01
  135 #define IN0BUF #0x7f00
  136 #define IN0BC #0x7fb5
  137 #define OUT0BUF #0x7ec0
  138 #define OUT0BC #0x7fc5		
  139 #define IN2BUF #0x7e00
  140 #define IN2BC #0x7fb9
  141 #define IN2CS #0x7fb8
  142 #define OUT2BC #0x7fc9
  143 #define OUT2CS #0x7fc8
  144 #define OUT2BUF #0x7dc0
  145 #define IN4BUF #0x7d00
  146 #define IN4BC #0x7fbd
  147 #define IN4CS #0x7fbc
  148 #define OEB #0x7f9d
  149 #define OUTB #0x7f97
  150 #define OEC #0x7f9e
  151 #define OUTC #0x7f98
  152 #define PINSC #0x7f9b
  153 #define 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
  181 rx_ring_in:	.byte 0
  182 rx_ring_out:	.byte 0
  183 tx_ring_in:	.byte 0
  184 tx_ring_out:	.byte 0
  185 tx_unthrottle_threshold:	.byte 0
  186 		
  187 	.org 0x100H		; wants to be on a page boundary
  188 USB_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 		
  217 start:	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
  232 clear_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
  239 clear_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 	;; Original source had:
  289 	;;mov a,#01010011		; mode 1, enable rx, clear int
  290 	;; This was presumably meant to be a binary constant, but it's
  291 	;; really decimal and out of 8-bit range.  as31 used to treat
  292 	;; it as 0 and that seems to have worked, so carry on with 0.
  293 	mov a,#0
  294 	mov SCON, a
  295 	;;  using timer2, in 16-bit baud-rate-generator mode
  296 	;;   (xtal 12MHz, internal fosc 24MHz)
  297 	;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
  298 	;;  57600: 0xFFF2.F, say 0xFFF3
  299 	;;   9600: 0xFFB1.E, say 0xFFB2
  300 	;;    300: 0xF63C
  301 #define BAUD 9600
  302 #define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
  303 #define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
  304 #define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
  305 		
  306 	mov T2CON, #030h	; rclk=1,tclk=1,cp=0,tr2=0(enable later)
  307 	mov r3, #5
  308 	acall set_baud
  309 	setb TR2
  310 	mov SCON, #050h
  311 	
  312 #if 0
  313 	mov r1, #0x40
  314 	mov a, #0x41
  315 send:	
  316 	mov SBUF, a
  317 	inc a
  318 	anl a, #0x3F
  319 	orl a, #0x40
  320 ;	xrl a, #0x02
  321 wait1:	
  322 	jnb TI, wait1
  323 	clr TI
  324 	djnz r1, send
  325 ;done:	sjmp done
  326 
  327 #endif
  328 	
  329 	setb EUSB
  330 	setb EA
  331 	setb ES0
  332 	;acall dump_stat
  333 
  334 	;; hey, what say we RENUMERATE! (TRM p.62)
  335 	mov a, #0
  336 	mov dps, a
  337 	mov dptr, USBCS
  338 	mov a, #0x02		; DISCON=0, DISCOE=0, RENUM=1
  339 	movx @dptr, a
  340 	;; now presence pin is floating, simulating disconnect. wait 0.5s
  341 	mov r1, #46
  342 renum_wait1:
  343 	mov r2, #0
  344 renum_wait2:
  345 	mov r3, #0
  346 renum_wait3:
  347 	djnz r3, renum_wait3
  348 	djnz r2, renum_wait2
  349 	djnz r1, renum_wait1	; wait about n*(256^2) 6MHz clocks
  350 	mov a, #0x06		; DISCON=0, DISCOE=1, RENUM=1
  351 	movx @dptr, a
  352 	;; we are back online. the host device will now re-query us
  353 	
  354 	
  355 main:	sjmp main
  356 
  357 	
  358 
  359 ISR_Sudav:
  360 	push dps
  361 	push dpl
  362 	push dph
  363 	push dpl1
  364 	push dph1
  365 	push acc
  366 	mov a,EXIF
  367 	clr acc.4
  368 	mov EXIF,a		; clear INT2 first
  369 	mov dptr, USBIRQ	; clear USB int
  370 	mov a,#01h
  371 	movx @dptr,a
  372 
  373 	;; get request type
  374 	mov dptr, SETUPDAT
  375 	movx a, @dptr
  376 	mov r1, a		; r1 = bmRequestType
  377 	inc dptr
  378 	movx a, @dptr
  379 	mov r2, a		; r2 = bRequest
  380 	inc dptr
  381 	movx a, @dptr
  382 	mov r3, a		; r3 = wValueL
  383 	inc dptr
  384 	movx a, @dptr
  385 	mov r4, a		; r4 = wValueH
  386 
  387 	;; main switch on bmRequest.type: standard or vendor
  388 	mov a, r1
  389 	anl a, #0x60
  390 	cjne a, #0x00, setup_bmreq_type_not_standard
  391 	;; standard request: now main switch is on bRequest
  392 	ljmp setup_bmreq_is_standard
  393 	
  394 setup_bmreq_type_not_standard:	
  395 	;; a still has bmreq&0x60
  396 	cjne a, #0x40, setup_bmreq_type_not_vendor
  397 	;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
  398 	;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
  399 	cjne r2, #0x00, setup_ctrl_not_00
  400 	;; 00 is set baud, wValue[0] has baud rate index
  401 	lcall set_baud		; index in r3, carry set if error
  402 	jc setup_bmreq_type_not_standard__do_stall
  403 	ljmp setup_done_ack
  404 setup_bmreq_type_not_standard__do_stall:
  405 	ljmp setup_stall
  406 setup_ctrl_not_00:
  407 	cjne r2, #0x01, setup_ctrl_not_01
  408 	;; 01 is reserved for set bits (parity). TODO
  409 	ljmp setup_stall
  410 setup_ctrl_not_01:
  411 	cjne r2, #0x02, setup_ctrl_not_02
  412 	;; 02 is set HW flow control. TODO
  413 	ljmp setup_stall
  414 setup_ctrl_not_02:
  415 	cjne r2, #0x03, setup_ctrl_not_03
  416 	;; 03 is control pins (RTS, DTR).
  417 	ljmp control_pins	; will jump to setup_done_ack,
  418 				;  or setup_return_one_byte
  419 setup_ctrl_not_03:
  420 	cjne r2, #0x04, setup_ctrl_not_04
  421 	;; 04 is send break (really "turn break on/off"). TODO
  422 	cjne r3, #0x00, setup_ctrl_do_break_on
  423 	;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
  424 	mov dptr, PORTCCFG
  425 	movx a, @dptr
  426 	orl a, #0x02
  427 	movx @dptr, a
  428 	ljmp setup_done_ack
  429 setup_ctrl_do_break_on:
  430 	;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
  431 	mov dptr, OUTC
  432 	movx a, @dptr
  433 	anl a, #0xfd		; ~0x02
  434 	movx @dptr, a
  435 	mov dptr, PORTCCFG
  436 	movx a, @dptr
  437 	anl a, #0xfd		; ~0x02
  438 	movx @dptr, a
  439 	ljmp setup_done_ack
  440 setup_ctrl_not_04:
  441 	cjne r2, #0x05, setup_ctrl_not_05
  442 	;; 05 is set desired interrupt bitmap. TODO
  443 	ljmp setup_stall
  444 setup_ctrl_not_05:
  445 	cjne r2, #0x06, setup_ctrl_not_06
  446 	;; 06 is query room
  447 	cjne r3, #0x00, setup_ctrl_06_not_00
  448 	;; 06, wValue[0]=0 is query write_room
  449 	mov a, tx_ring_out
  450 	setb c
  451 	subb a, tx_ring_in	; out-1-in = 255 - (in-out)
  452 	ljmp setup_return_one_byte
  453 setup_ctrl_06_not_00:
  454 	cjne r3, #0x01, setup_ctrl_06_not_01
  455 	;; 06, wValue[0]=1 is query chars_in_buffer
  456 	mov a, tx_ring_in
  457 	clr c
  458 	subb a, tx_ring_out	; in-out
  459 	ljmp setup_return_one_byte
  460 setup_ctrl_06_not_01:	
  461 	ljmp setup_stall
  462 setup_ctrl_not_06:
  463 	cjne r2, #0x07, setup_ctrl_not_07
  464 	;; 07 is request tx unthrottle interrupt
  465 	mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
  466 	ljmp setup_done_ack
  467 setup_ctrl_not_07:
  468 	ljmp setup_stall
  469 	
  470 setup_bmreq_type_not_vendor:
  471 	ljmp setup_stall
  472 
  473 
  474 setup_bmreq_is_standard:	
  475 	cjne r2, #0x00, setup_breq_not_00
  476 	;; 00:	Get_Status (sub-switch on bmRequestType: device, ep, int)
  477 	cjne r1, #0x80, setup_Get_Status_not_device
  478 	;; Get_Status(device)
  479 	;;  are we self-powered? no. can we do remote wakeup? no
  480 	;;   so return two zero bytes. This is reusable
  481 setup_return_two_zero_bytes:
  482 	mov dptr, IN0BUF
  483 	clr a
  484 	movx @dptr, a
  485 	inc dptr
  486 	movx @dptr, a
  487 	mov dptr, IN0BC
  488 	mov a, #2
  489 	movx @dptr, a
  490 	ljmp setup_done_ack
  491 setup_Get_Status_not_device:
  492 	cjne r1, #0x82, setup_Get_Status_not_endpoint
  493 	;; Get_Status(endpoint)
  494 	;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
  495 	;; for now: cheat. TODO
  496 	sjmp setup_return_two_zero_bytes
  497 setup_Get_Status_not_endpoint:
  498 	cjne r1, #0x81, setup_Get_Status_not_interface
  499 	;; Get_Status(interface): return two zeros
  500 	sjmp setup_return_two_zero_bytes
  501 setup_Get_Status_not_interface:	
  502 	ljmp setup_stall
  503 	
  504 setup_breq_not_00:
  505 	cjne r2, #0x01, setup_breq_not_01
  506 	;; 01:	Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
  507 	cjne r3, #0x00, setup_Clear_Feature_not_stall
  508 	;; Clear_Feature(stall). should clear a stall bit. TODO
  509 	ljmp setup_stall
  510 setup_Clear_Feature_not_stall:
  511 	cjne r3, #0x01, setup_Clear_Feature_not_rwake
  512 	;; Clear_Feature(remote wakeup). ignored.
  513 	ljmp setup_done_ack
  514 setup_Clear_Feature_not_rwake:
  515 	ljmp setup_stall
  516 	
  517 setup_breq_not_01:
  518 	cjne r2, #0x03, setup_breq_not_03
  519 	;; 03:	Set_Feature (sub-switch on wValueL: stall, remote wakeup)
  520 	cjne r3, #0x00, setup_Set_Feature_not_stall
  521 	;; Set_Feature(stall). Should set a stall bit. TODO
  522 	ljmp setup_stall
  523 setup_Set_Feature_not_stall:
  524 	cjne r3, #0x01, setup_Set_Feature_not_rwake
  525 	;; Set_Feature(remote wakeup). ignored.
  526 	ljmp setup_done_ack
  527 setup_Set_Feature_not_rwake:
  528 	ljmp setup_stall
  529 	
  530 setup_breq_not_03:	
  531 	cjne r2, #0x06, setup_breq_not_06
  532 	;; 06:	Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
  533 	cjne r4, #0x01, setup_Get_Descriptor_not_device
  534 	;; Get_Descriptor(device)
  535 	mov dptr, SUDPTRH
  536 	mov a, #HIGH(desc_device)
  537 	movx @dptr, a
  538 	mov dptr, SUDPTRL
  539 	mov a, #LOW(desc_device)
  540 	movx @dptr, a
  541 	ljmp setup_done_ack
  542 setup_Get_Descriptor_not_device:
  543 	cjne r4, #0x02, setup_Get_Descriptor_not_config
  544 	;; Get_Descriptor(config[n])
  545 	cjne r3, #0x00, setup_stall; only handle n==0
  546 	;; Get_Descriptor(config[0])
  547 	mov dptr, SUDPTRH
  548 	mov a, #HIGH(desc_config1)
  549 	movx @dptr, a
  550 	mov dptr, SUDPTRL
  551 	mov a, #LOW(desc_config1)
  552 	movx @dptr, a
  553 	ljmp setup_done_ack
  554 setup_Get_Descriptor_not_config:
  555 	cjne r4, #0x03, setup_Get_Descriptor_not_string
  556 	;; Get_Descriptor(string[wValueL])
  557 	;;  if (wValueL >= maxstrings) stall
  558 	mov a, #((desc_strings_end-desc_strings)/2)
  559 	clr c
  560 	subb a,r3		; a=4, r3 = 0..3 . if a<=0 then stall
  561 	jc  setup_stall
  562 	jz  setup_stall
  563 	mov a, r3
  564 	add a, r3		; a = 2*wValueL
  565 	mov dptr, #desc_strings
  566 	add a, dpl
  567 	mov dpl, a
  568 	mov a, #0
  569 	addc a, dph
  570 	mov dph, a		; dph = desc_strings[a]. big endian! (handy)
  571 	;; it looks like my adapter uses a revision of the EZUSB that
  572 	;; contains "rev D errata number 8", as hinted in the EzUSB example
  573 	;; code. I cannot find an actual errata description on the Cypress
  574 	;; web site, but from the example code it looks like this bug causes
  575 	;; the length of string descriptors to be read incorrectly, possibly
  576 	;; sending back more characters than the descriptor has. The workaround
  577 	;; is to manually send out all of the data. The consequence of not
  578 	;; using the workaround is that the strings gathered by the kernel
  579 	;; driver are too long and are filled with trailing garbage (including
  580 	;; leftover strings). Writing this out by hand is a nuisance, so for
  581 	;; now I will just live with the bug.
  582 	movx a, @dptr
  583 	mov r1, a
  584 	inc dptr
  585 	movx a, @dptr
  586 	mov r2, a
  587 	mov dptr, SUDPTRH
  588 	mov a, r1
  589 	movx @dptr, a
  590 	mov dptr, SUDPTRL
  591 	mov a, r2
  592 	movx @dptr, a
  593 	;; done
  594 	ljmp setup_done_ack
  595 	
  596 setup_Get_Descriptor_not_string:
  597 	ljmp setup_stall
  598 	
  599 setup_breq_not_06:
  600 	cjne r2, #0x08, setup_breq_not_08
  601 	;; Get_Configuration. always 1. return one byte.
  602 	;; this is reusable
  603 	mov a, #1
  604 setup_return_one_byte:	
  605 	mov dptr, IN0BUF
  606 	movx @dptr, a
  607 	mov a, #1
  608 	mov dptr, IN0BC
  609 	movx @dptr, a
  610 	ljmp setup_done_ack
  611 setup_breq_not_08:
  612 	cjne r2, #0x09, setup_breq_not_09
  613 	;; 09: Set_Configuration. ignored.
  614 	ljmp setup_done_ack
  615 setup_breq_not_09:
  616 	cjne r2, #0x0a, setup_breq_not_0a
  617 	;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
  618 	;;  since we only have one interface, ignore wIndexL, return a 0
  619 	mov a, #0
  620 	ljmp setup_return_one_byte
  621 setup_breq_not_0a:
  622 	cjne r2, #0x0b, setup_breq_not_0b
  623 	;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
  624 	ljmp setup_done_ack
  625 setup_breq_not_0b:
  626 	ljmp setup_stall
  627 
  628 		
  629 setup_done_ack:	
  630 	;; now clear HSNAK
  631 	mov dptr, EP0CS
  632 	mov a, #0x02
  633 	movx @dptr, a
  634 	sjmp setup_done
  635 setup_stall:	
  636 	;; unhandled. STALL
  637 	;EP0CS |= bmEPSTALL
  638 	mov dptr, EP0CS
  639 	movx a, @dptr
  640 	orl a, EP0STALLbit
  641 	movx @dptr, a
  642 	sjmp setup_done
  643 	
  644 setup_done:	
  645 	pop acc
  646 	pop dph1
  647 	pop dpl1
  648 	pop dph
  649 	pop dpl
  650 	pop dps
  651 	reti
  652 
  653 ;;; ==============================================================
  654 	
  655 set_baud:			; baud index in r3
  656 	;; verify a < 10
  657 	mov a, r3
  658 	jb ACC.7, set_baud__badbaud
  659 	clr c
  660 	subb a, #10
  661 	jnc set_baud__badbaud
  662 	mov a, r3
  663 	rl a			; a = index*2
  664 	add a, #LOW(baud_table)
  665 	mov dpl, a
  666 	mov a, #HIGH(baud_table)
  667 	addc a, #0
  668 	mov dph, a
  669 	;; TODO: shut down xmit/receive
  670 	;; TODO: wait for current xmit char to leave
  671 	;; TODO: shut down timer to avoid partial-char glitch
  672 	movx a,@dptr		; BAUD_HIGH
  673 	mov RCAP2H, a
  674 	mov TH2, a
  675 	inc dptr
  676 	movx a,@dptr		; BAUD_LOW
  677 	mov RCAP2L, a
  678 	mov TL2, a
  679 	;; TODO: restart xmit/receive
  680 	;; TODO: reenable interrupts, resume tx if pending
  681 	clr c			; c=0: success
  682 	ret
  683 set_baud__badbaud:
  684 	setb c			; c=1: failure
  685 	ret
  686 	
  687 ;;; ==================================================
  688 control_pins:
  689 	cjne r1, #0x41, control_pins_in
  690 control_pins_out:
  691 	mov a, r3 ; wValue[0] holds new bits:	b7 is new DTR, b2 is new RTS
  692 	xrl a, #0xff		; 1 means active, 0V, +12V ?
  693 	anl a, #0x84
  694 	mov r3, a
  695 	mov dptr, OUTC
  696 	movx a, @dptr		; only change bits 7 and 2
  697 	anl a, #0x7b		; ~0x84
  698 	orl a, r3
  699 	movx @dptr, a		; other pins are inputs, bits ignored
  700 	ljmp setup_done_ack
  701 control_pins_in:
  702 	mov dptr, PINSC
  703 	movx a, @dptr
  704 	xrl a, #0xff
  705 	ljmp setup_return_one_byte
  706 
  707 ;;; ========================================
  708 	
  709 ISR_Ep2in:
  710 	push dps
  711 	push dpl
  712 	push dph
  713 	push dpl1
  714 	push dph1
  715 	push acc
  716 	mov a,EXIF
  717 	clr acc.4
  718 	mov EXIF,a		; clear INT2 first
  719 	mov dptr, IN07IRQ	; clear USB int
  720 	mov a,#04h
  721 	movx @dptr,a
  722 
  723 	;; do stuff
  724 	lcall start_in
  725 	
  726 	pop acc
  727 	pop dph1
  728 	pop dpl1
  729 	pop dph
  730 	pop dpl
  731 	pop dps
  732 	reti
  733 
  734 ISR_Ep2out:
  735 	push dps
  736 	push dpl
  737 	push dph
  738 	push dpl1
  739 	push dph1
  740 	push acc
  741 	mov a,EXIF
  742 	clr acc.4
  743 	mov EXIF,a		; clear INT2 first
  744 	mov dptr, OUT07IRQ	; clear USB int
  745 	mov a,#04h
  746 	movx @dptr,a
  747 
  748 	;; do stuff
  749 
  750 	;; copy data into buffer. for now, assume we will have enough space
  751 	mov dptr, OUT2BC	; get byte count
  752 	movx a,@dptr
  753 	mov r1, a
  754 	clr a
  755 	mov dps, a
  756 	mov dptr, OUT2BUF	; load DPTR0 with source
  757 	mov dph1, #HIGH(tx_ring)	; load DPTR1 with target
  758 	mov dpl1, tx_ring_in
  759 OUT_loop:
  760 	movx a,@dptr		; read
  761 	inc dps			; switch to DPTR1: target
  762 	inc dpl1		; target = tx_ring_in+1
  763 	movx @dptr,a		; store
  764 	mov a,dpl1
  765 	cjne a, tx_ring_out, OUT_no_overflow
  766 	sjmp OUT_overflow
  767 OUT_no_overflow:	
  768 	inc tx_ring_in		; tx_ring_in++
  769 	inc dps			; switch to DPTR0: source
  770 	inc dptr
  771 	djnz r1, OUT_loop
  772 	sjmp OUT_done
  773 OUT_overflow:
  774 	;; signal overflow
  775 	;; fall through
  776 OUT_done:	
  777 	;; ack
  778 	mov dptr,OUT2BC
  779 	movx @dptr,a
  780 
  781 	;; start tx
  782 	acall maybe_start_tx
  783 	;acall dump_stat
  784 	
  785 	pop acc
  786 	pop dph1
  787 	pop dpl1
  788 	pop dph
  789 	pop dpl
  790 	pop dps
  791 	reti
  792 
  793 dump_stat:
  794 	;; fill in EP4in with a debugging message:
  795 	;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
  796 	;;   tx_active
  797 	;;   tx_ring[0..15]
  798 	;;   0xfc
  799 	;;   rx_ring[0..15]
  800 	clr a
  801 	mov dps, a
  802 	
  803 	mov dptr, IN4CS
  804 	movx a, @dptr
  805 	jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
  806 	mov dptr, IN4BUF
  807 	
  808 	mov a, tx_ring_in
  809 	movx @dptr, a
  810 	inc dptr
  811 	mov a, tx_ring_out
  812 	movx @dptr, a
  813 	inc dptr
  814 
  815 	mov a, rx_ring_in
  816 	movx @dptr, a
  817 	inc dptr
  818 	mov a, rx_ring_out
  819 	movx @dptr, a
  820 	inc dptr
  821 	
  822 	clr a
  823 	jnb TX_RUNNING, dump_stat__no_tx_running
  824 	inc a
  825 dump_stat__no_tx_running:
  826 	movx @dptr, a
  827 	inc dptr
  828 	;; tx_ring[0..15]
  829 	inc dps
  830 	mov dptr, #tx_ring	; DPTR1: source
  831 	mov r1, #16
  832 dump_stat__tx_ring_loop:
  833 	movx a, @dptr
  834 	inc dptr
  835 	inc dps
  836 	movx @dptr, a
  837 	inc dptr
  838 	inc dps
  839 	djnz r1, dump_stat__tx_ring_loop
  840 	inc dps
  841 	
  842 	mov a, #0xfc
  843 	movx @dptr, a
  844 	inc dptr
  845 	
  846 	;; rx_ring[0..15]
  847 	inc dps
  848 	mov dptr, #rx_ring	; DPTR1: source
  849 	mov r1, #16
  850 dump_stat__rx_ring_loop:
  851 	movx a, @dptr
  852 	inc dptr
  853 	inc dps
  854 	movx @dptr, a
  855 	inc dptr
  856 	inc dps
  857 	djnz r1, dump_stat__rx_ring_loop
  858 	
  859 	;; now send it
  860 	clr a
  861 	mov dps, a
  862 	mov dptr, IN4BC
  863 	mov a, #38
  864 	movx @dptr, a
  865 dump_stat__done:	
  866 	ret
  867 		
  868 ;;; ============================================================
  869 	
  870 maybe_start_tx:
  871 	;; make sure the tx process is running.
  872 	jb TX_RUNNING, start_tx_done
  873 start_tx:
  874 	;; is there work to be done?
  875 	mov a, tx_ring_in
  876 	cjne a,tx_ring_out, start_tx__work
  877 	ret			; no work
  878 start_tx__work:	
  879 	;; tx was not running. send the first character, setup the TI int
  880 	inc tx_ring_out		; [++tx_ring_out]
  881 	mov dph, #HIGH(tx_ring)
  882 	mov dpl, tx_ring_out
  883 	movx a, @dptr
  884 	mov sbuf, a
  885 	setb TX_RUNNING
  886 start_tx_done:
  887 	;; can we unthrottle the host tx process?
  888 	;;  step 1: do we care?
  889 	mov a, #0
  890 	cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
  891 	;; nope
  892 start_tx_really_done:
  893 	ret
  894 start_tx__maybe_unthrottle_tx:
  895 	;;  step 2: is there now room?
  896 	mov a, tx_ring_out
  897 	setb c
  898 	subb a, tx_ring_in
  899 	;; a is now write_room. If thresh >= a, we can unthrottle
  900 	clr c
  901 	subb a, tx_unthrottle_threshold
  902 	jc start_tx_really_done	; nope
  903 	;; yes, we can unthrottle. remove the threshold and mark a request
  904 	mov tx_unthrottle_threshold, #0
  905 	setb DO_TX_UNTHROTTLE
  906 	;; prod rx, which will actually send the message when in2 becomes free
  907 	ljmp start_in
  908 	
  909 
  910 serial_int:
  911 	push dps
  912 	push dpl
  913 	push dph
  914 	push dpl1
  915 	push dph1
  916 	push acc
  917 	jnb TI, serial_int__not_tx
  918 	;; tx finished. send another character if we have one
  919 	clr TI			; clear int
  920 	clr TX_RUNNING
  921 	lcall start_tx
  922 serial_int__not_tx:
  923 	jnb RI, serial_int__not_rx
  924 	lcall get_rx_char
  925 	clr RI			; clear int
  926 serial_int__not_rx:	
  927 	;; return
  928 	pop acc
  929 	pop dph1
  930 	pop dpl1
  931 	pop dph
  932 	pop dpl
  933 	pop dps
  934 	reti
  935 
  936 get_rx_char:
  937 	mov dph, #HIGH(rx_ring)
  938 	mov dpl, rx_ring_in
  939 	inc dpl			; target = rx_ring_in+1
  940 	mov a, sbuf
  941 	movx @dptr, a
  942 	;; check for overflow before incrementing rx_ring_in
  943 	mov a, dpl
  944 	cjne a, rx_ring_out, get_rx_char__no_overflow
  945 	;; signal overflow
  946 	ret
  947 get_rx_char__no_overflow:	
  948 	inc rx_ring_in
  949 	;; kick off USB INpipe
  950 	acall start_in
  951 	ret
  952 
  953 start_in:
  954 	;; check if the inpipe is already running.
  955 	mov dptr, IN2CS
  956 	movx a, @dptr
  957 	jb acc.1, start_in__done; int will handle it
  958 	jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
  959 	;; see if there is any work to do. a serial interrupt might occur
  960 	;; during this sequence?
  961 	mov a, rx_ring_in
  962 	cjne a, rx_ring_out, start_in__have_work
  963 	ret			; nope
  964 start_in__have_work:	
  965 	;; now copy as much data as possible into the pipe. 63 bytes max.
  966 	clr a
  967 	mov dps, a
  968 	mov dph, #HIGH(rx_ring)	; load DPTR0 with source
  969 	inc dps
  970 	mov dptr, IN2BUF	; load DPTR1 with target
  971 	movx @dptr, a		; in[0] signals that rest of IN is rx data
  972 	inc dptr
  973 	inc dps
  974 	;; loop until we run out of data, or we have copied 64 bytes
  975 	mov r1, #1		; INbuf size counter
  976 start_in__loop:
  977 	mov a, rx_ring_in
  978 	cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
  979 	sjmp start_in__kick
  980 start_inlocal_irq_enablell_copying:
  981 	inc rx_ring_out
  982 	mov dpl, rx_ring_out
  983 	movx a, @dptr
  984 	inc dps
  985 	movx @dptr, a		; write into IN buffer
  986 	inc dptr
  987 	inc dps
  988 	inc r1
  989 	cjne r1, #64, start_in__loop; loop
  990 start_in__kick:
  991 	;; either we ran out of data, or we copied 64 bytes. r1 has byte count
  992 	;; kick off IN
  993 	mov dptr, IN2BC
  994 	mov a, r1
  995 	jz start_in__done
  996 	movx @dptr, a
  997 	;; done
  998 start_in__done:
  999 	;acall dump_stat
 1000 	ret
 1001 start_in__do_tx_unthrottle:
 1002 	;; special sequence: send a tx unthrottle message
 1003 	clr DO_TX_UNTHROTTLE
 1004 	clr a
 1005 	mov dps, a
 1006 	mov dptr, IN2BUF
 1007 	mov a, #1
 1008 	movx @dptr, a
 1009 	inc dptr
 1010 	mov a, #2
 1011 	movx @dptr, a
 1012 	mov dptr, IN2BC
 1013 	movx @dptr, a
 1014 	ret
 1015 	
 1016 putchar:
 1017 	clr TI
 1018 	mov SBUF, a
 1019 putchar_wait:
 1020 	jnb TI, putchar_wait
 1021 	clr TI
 1022 	ret
 1023 
 1024 	
 1025 baud_table:			; baud_high, then baud_low
 1026 	;; baud[0]: 110
 1027 	.byte BAUD_HIGH(110)
 1028 	.byte BAUD_LOW(110)
 1029 	;; baud[1]: 300
 1030 	.byte BAUD_HIGH(300)
 1031 	.byte BAUD_LOW(300)
 1032 	;; baud[2]: 1200
 1033 	.byte BAUD_HIGH(1200)
 1034 	.byte BAUD_LOW(1200)
 1035 	;; baud[3]: 2400
 1036 	.byte BAUD_HIGH(2400)
 1037 	.byte BAUD_LOW(2400)
 1038 	;; baud[4]: 4800
 1039 	.byte BAUD_HIGH(4800)
 1040 	.byte BAUD_LOW(4800)
 1041 	;; baud[5]: 9600
 1042 	.byte BAUD_HIGH(9600)
 1043 	.byte BAUD_LOW(9600)
 1044 	;; baud[6]: 19200
 1045 	.byte BAUD_HIGH(19200)
 1046 	.byte BAUD_LOW(19200)
 1047 	;; baud[7]: 38400
 1048 	.byte BAUD_HIGH(38400)
 1049 	.byte BAUD_LOW(38400)
 1050 	;; baud[8]: 57600
 1051 	.byte BAUD_HIGH(57600)
 1052 	.byte BAUD_LOW(57600)
 1053 	;; baud[9]: 115200
 1054 	.byte BAUD_HIGH(115200)
 1055 	.byte BAUD_LOW(115200)
 1056 
 1057 desc_device:
 1058 	.byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
 1059 	.byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
 1060 ;;; The "real" device id, which must match the host driver, is that
 1061 ;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
 1062 	
 1063 desc_config1:
 1064 	.byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
 1065 	.byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
 1066 	.byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
 1067 	.byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
 1068 
 1069 desc_strings:
 1070 	.word string_langids, string_mfg, string_product, string_serial
 1071 desc_strings_end:
 1072 
 1073 string_langids:	.byte string_langids_end-string_langids
 1074 	.byte 3
 1075 	.word 0
 1076 string_langids_end:
 1077 
 1078 	;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
 1079 	;; *that* is a pain in the ass to encode. And they are little-endian
 1080 	;; too. Use this perl snippet to get the bytecodes:
 1081 	/* while (<>) {
 1082 	    @c = split(//);
 1083 	    foreach $c (@c) {
 1084 	     printf("0x%02x, 0x00, ", ord($c));
 1085 	    }
 1086 	   }
 1087 	*/
 1088 
 1089 string_mfg:	.byte string_mfg_end-string_mfg
 1090 	.byte 3
 1091 ;	.byte "ACME usb widgets"
 1092 	.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
 1093 string_mfg_end:
 1094 	
 1095 string_product:	.byte string_product_end-string_product
 1096 	.byte 3
 1097 ;	.byte "ACME USB serial widget"
 1098 	.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
 1099 string_product_end:
 1100 	
 1101 string_serial:	.byte string_serial_end-string_serial
 1102 	.byte 3
 1103 ;	.byte "47"
 1104 	.byte 0x34, 0x00, 0x37, 0x00
 1105 string_serial_end:
 1106 		
 1107 ;;; ring buffer memory
 1108 	;; tx_ring_in+1 is where the next input byte will go
 1109 	;; [tx_ring_out] has been sent
 1110 	;; if tx_ring_in == tx_ring_out, theres no work to do
 1111 	;; there are (tx_ring_in - tx_ring_out) chars to be written
 1112 	;; dont let _in lap _out
 1113 	;;   cannot inc if tx_ring_in+1 == tx_ring_out
 1114 	;;  write [tx_ring_in+1] then tx_ring_in++
 1115 	;;   if (tx_ring_in+1 == tx_ring_out), overflow
 1116 	;;   else tx_ring_in++
 1117 	;;  read/send [tx_ring_out+1], then tx_ring_out++
 1118 
 1119 	;; rx_ring_in works the same way
 1120 	
 1121 	.org 0x1000
 1122 tx_ring:
 1123 	.skip 0x100		; 256 bytes
 1124 rx_ring:
 1125 	.skip 0x100		; 256 bytes
 1126 	
 1127 	
 1128 	.END
 1129 	

Generated by cgit