MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / usb / serial / xircom_pgs.S
blob05d99dd637766a7fd63a5879a57adb21e4f4eeeb
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.
108  * Questions, bugs, patches to Brian.
110  *  -Brian Warner <warner@lothar.com>
112  */
113         
114 #define HIGH(x) (((x) & 0xff00) / 256)
115 #define LOW(x) ((x) & 0xff)
117 #define dpl1 0x84
118 #define dph1 0x85
119 #define dps 0x86
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
128 #define EXIF 0x91
129 #define EIE 0xe8
130         .flag EUSB, EIE.0
131         .flag ES0, IE.4
133 #define EP0CS #0x7fb4
134 #define EP0STALLbit #0x01
135 #define IN0BUF #0x7f00
136 #define IN0BC #0x7fb5
137 #define OUT0BUF #0x7ec0
138 #define OUT0BC #0x7fc5          
139 #define IN2BUF #0x7e00
140 #define IN2BC #0x7fb9
141 #define IN2CS #0x7fb8
142 #define OUT2BC #0x7fc9
143 #define OUT2CS #0x7fc8
144 #define OUT2BUF #0x7dc0
145 #define IN4BUF #0x7d00
146 #define IN4BC #0x7fbd
147 #define IN4CS #0x7fbc
148 #define OEB #0x7f9d
149 #define OUTB #0x7f97
150 #define OEC #0x7f9e
151 #define OUTC #0x7f98
152 #define PINSC #0x7f9b
153 #define PORTBCFG #0x7f94
154 #define PORTCCFG #0x7f95
155 #define OEA     #0x7f9c
156 #define IN07IRQ #0x7fa9
157 #define OUT07IRQ #0x7faa
158 #define IN07IEN #0x7fac
159 #define OUT07IEN #0x7fad
160 #define USBIRQ #0x7fab
161 #define USBIEN #0x7fae
162 #define USBBAV #0x7faf
163 #define USBCS #0x7fd6
164 #define SUDPTRH #0x7fd4
165 #define SUDPTRL #0x7fd5
166 #define SETUPDAT #0x7fe8
167                 
168         ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
170         .org 0
171         ljmp start
172         ;; interrupt vectors
173         .org 23H
174         ljmp serial_int
175         .byte 0
176         
177         .org 43H
178         ljmp USB_Jump_Table
179         .byte 0                 ; filled in by the USB core
181 ;;; local variables. These are not initialized properly: do it by hand.
182         .org 30H
183 rx_ring_in:     .byte 0
184 rx_ring_out:    .byte 0
185 tx_ring_in:     .byte 0
186 tx_ring_out:    .byte 0
187 tx_unthrottle_threshold:        .byte 0
188                 
189         .org 0x100H             ; wants to be on a page boundary
190 USB_Jump_Table:
191         ljmp    ISR_Sudav       ; Setup Data Available
192         .byte 0
193         ljmp    0               ; Start of Frame
194         .byte 0
195         ljmp    0               ; Setup Data Loading
196         .byte 0
197         ljmp    0               ; Global Suspend
198         .byte   0
199         ljmp    0               ; USB Reset     
200         .byte   0
201         ljmp    0               ; Reserved
202         .byte   0
203         ljmp    0               ; End Point 0 In
204         .byte   0
205         ljmp    0               ; End Point 0 Out
206         .byte   0
207         ljmp    0               ; End Point 1 In
208         .byte   0
209         ljmp    0               ; End Point 1 Out
210         .byte   0
211         ljmp    ISR_Ep2in
212         .byte   0
213         ljmp    ISR_Ep2out
214         .byte   0
217         .org 0x200
218                 
219 start:  mov SP,STACK-1 ; set stack
220         ;; clear local variables
221         clr a
222         mov tx_ring_in, a
223         mov tx_ring_out, a
224         mov rx_ring_in, a
225         mov rx_ring_out, a
226         mov tx_unthrottle_threshold, a
227         clr TX_RUNNING
228         clr DO_TX_UNTHROTTLE
229         
230         ;; clear fifo with "fe"
231         mov r1, 0
232         mov a, #0xfe
233         mov dptr, #tx_ring
234 clear_tx_ring_loop:
235         movx @dptr, a
236         inc dptr
237         djnz r1, clear_tx_ring_loop
239         mov a, #0xfd
240         mov dptr, #rx_ring
241 clear_rx_ring_loop:
242         movx @dptr, a
243         inc dptr
244         djnz r1, clear_rx_ring_loop
246 ;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
247 ;;; on Xircom the STANDBY is wired to PB6 and PC4 
248         mov dptr, PORTBCFG
249         mov a, #0xBf
250         movx @dptr, a
251         mov dptr, PORTCCFG
252         mov a, #0xef
253         movx @dptr, a
254         
255         ;; set OEC.4
256         mov a, #0x10
257         mov dptr,OEC
258         movx @dptr,a
260         ;; clear PC4
261         mov a, #0x00
262         mov dptr,OUTC
263         movx @dptr,a
265         ;; set OEB.6
266         mov a, #0x40
267         mov dptr,OEB
268         movx @dptr,a
270         ;; clear PB6
271         mov a, #0x00
272         mov dptr,OUTB
273         movx @dptr,a
275         ;; set OEC.[17]
276         mov a, #0x82
277         mov dptr,OEC
278         movx @dptr,a
281         ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
282         mov dptr, PORTCCFG
283         mov a, #0x03
284         movx @dptr, a
285         
286         ;; set up interrupts, autovectoring
287         ;; set BKPT
288         mov dptr, USBBAV
289         movx a,@dptr
290         setb acc.0              ; AVEN bit to 0
291         movx @dptr, a
293         mov a,#0x01             ; enable SUDAV: setup data available (for ep0)
294         mov dptr, USBIRQ
295         movx @dptr, a           ; clear SUDAVI
296         mov dptr, USBIEN
297         movx @dptr, a
298         
299         mov dptr, IN07IEN
300         mov a,#0x04             ; enable IN2 int
301         movx @dptr, a
302         
303         mov dptr, OUT07IEN
304         mov a,#0x04             ; enable OUT2 int
305         movx @dptr, a
306         mov dptr, OUT2BC
307         movx @dptr, a           ; arm OUT2
309 ;;      mov a, #0x84            ; turn on RTS, DTR
310 ;;      mov dptr,OUTC
311 ;;      movx @dptr, a
313         mov a, #0x7             ; turn on  DTR
314         mov dptr,USBBAV
315         movx @dptr, a
317         mov a, #0x20             ; turn on the RED led 
318         mov dptr,OEA
319         movx @dptr, a
321         mov a, #0x80            ; turn on  RTS
322         mov dptr,OUTC
323         movx @dptr, a
325         ;; setup the serial port. 9600 8N1.
326         mov a,#0x53             ; mode 1, enable rx, clear int
327         mov SCON, a
328         ;;  using timer2, in 16-bit baud-rate-generator mode
329         ;;   (xtal 12MHz, internal fosc 24MHz)
330         ;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
331         ;;  57600: 0xFFF2.F, say 0xFFF3
332         ;;   9600: 0xFFB1.E, say 0xFFB2
333         ;;    300: 0xF63C
334 #define BAUD 9600
335 #define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
336 #define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
337 #define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
338                 
339         mov T2CON, #030h        ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
340         mov r3, #5
341         acall set_baud
342         setb TR2
343         mov SCON, #050h
344         
345 #if 0
346         mov r1, #0x40
347         mov a, #0x41
348 send:   
349         mov SBUF, a
350         inc a
351         anl a, #0x3F
352         orl a, #0x40
353 ;       xrl a, #0x02
354 wait1:  
355         jnb TI, wait1
356         clr TI
357         djnz r1, send
358 ;done:  sjmp done
360 #endif
361         
362         setb EUSB
363         setb EA
364         setb ES0
365         ;acall dump_stat
367         ;; hey, what say we RENUMERATE! (TRM p.62)
368         mov a, #0
369         mov dps, a
370         mov dptr, USBCS
371         mov a, #0x02            ; DISCON=0, DISCOE=0, RENUM=1
372         movx @dptr, a
373         ;; now presence pin is floating, simulating disconnect. wait 0.5s
374         mov r1, #46
375 renum_wait1:
376         mov r2, #0
377 renum_wait2:
378         mov r3, #0
379 renum_wait3:
380         djnz r3, renum_wait3
381         djnz r2, renum_wait2
382         djnz r1, renum_wait1    ; wait about n*(256^2) 6MHz clocks
383         mov a, #0x06            ; DISCON=0, DISCOE=1, RENUM=1
384         movx @dptr, a
385         ;; we are back online. the host device will now re-query us
386         
387         
388 main:   sjmp main
390         
392 ISR_Sudav:
393         push dps
394         push dpl
395         push dph
396         push dpl1
397         push dph1
398         push acc
399         mov a,EXIF
400         clr acc.4
401         mov EXIF,a              ; clear INT2 first
402         mov dptr, USBIRQ        ; clear USB int
403         mov a,#01h
404         movx @dptr,a
406         ;; get request type
407         mov dptr, SETUPDAT
408         movx a, @dptr
409         mov r1, a               ; r1 = bmRequestType
410         inc dptr
411         movx a, @dptr
412         mov r2, a               ; r2 = bRequest
413         inc dptr
414         movx a, @dptr
415         mov r3, a               ; r3 = wValueL
416         inc dptr
417         movx a, @dptr
418         mov r4, a               ; r4 = wValueH
420         ;; main switch on bmRequest.type: standard or vendor
421         mov a, r1
422         anl a, #0x60
423         cjne a, #0x00, setup_bmreq_type_not_standard
424         ;; standard request: now main switch is on bRequest
425         ljmp setup_bmreq_is_standard
426         
427 setup_bmreq_type_not_standard:  
428         ;; a still has bmreq&0x60
429         cjne a, #0x40, setup_bmreq_type_not_vendor
430         ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
431         ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
432         cjne r2, #0x00, setup_ctrl_not_00
433         ;; 00 is set baud, wValue[0] has baud rate index
434         lcall set_baud          ; index in r3, carry set if error
435         jc setup_bmreq_type_not_standard__do_stall
436         ljmp setup_done_ack
437 setup_bmreq_type_not_standard__do_stall:
438         ljmp setup_stall
439 setup_ctrl_not_00:
440         cjne r2, #0x01, setup_ctrl_not_01
441         ;; 01 is reserved for set bits (parity). TODO
442         ljmp setup_stall
443 setup_ctrl_not_01:
444         cjne r2, #0x02, setup_ctrl_not_02
445         ;; 02 is set HW flow control. TODO
446         ljmp setup_stall
447 setup_ctrl_not_02:
448         cjne r2, #0x03, setup_ctrl_not_03
449         ;; 03 is control pins (RTS, DTR).
450         ljmp control_pins       ; will jump to setup_done_ack,
451                                 ;  or setup_return_one_byte
452 setup_ctrl_not_03:
453         cjne r2, #0x04, setup_ctrl_not_04
454         ;; 04 is send break (really "turn break on/off"). TODO
455         cjne r3, #0x00, setup_ctrl_do_break_on
456         ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
457         mov dptr, PORTCCFG
458         movx a, @dptr
459         orl a, #0x02
460         movx @dptr, a
461         ljmp setup_done_ack
462 setup_ctrl_do_break_on:
463         ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
464         mov dptr, OUTC
465         movx a, @dptr
466         anl a, #0xfd            ; ~0x02
467         movx @dptr, a
468         mov dptr, PORTCCFG
469         movx a, @dptr
470         anl a, #0xfd            ; ~0x02
471         movx @dptr, a
472         ljmp setup_done_ack
473 setup_ctrl_not_04:
474         cjne r2, #0x05, setup_ctrl_not_05
475         ;; 05 is set desired interrupt bitmap. TODO
476         ljmp setup_stall
477 setup_ctrl_not_05:
478         cjne r2, #0x06, setup_ctrl_not_06
479         ;; 06 is query room
480         cjne r3, #0x00, setup_ctrl_06_not_00
481         ;; 06, wValue[0]=0 is query write_room
482         mov a, tx_ring_out
483         setb c
484         subb a, tx_ring_in      ; out-1-in = 255 - (in-out)
485         ljmp setup_return_one_byte
486 setup_ctrl_06_not_00:
487         cjne r3, #0x01, setup_ctrl_06_not_01
488         ;; 06, wValue[0]=1 is query chars_in_buffer
489         mov a, tx_ring_in
490         clr c
491         subb a, tx_ring_out     ; in-out
492         ljmp setup_return_one_byte
493 setup_ctrl_06_not_01:   
494         ljmp setup_stall
495 setup_ctrl_not_06:
496         cjne r2, #0x07, setup_ctrl_not_07
497         ;; 07 is request tx unthrottle interrupt
498         mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
499         ljmp setup_done_ack
500 setup_ctrl_not_07:
501         ljmp setup_stall
502         
503 setup_bmreq_type_not_vendor:
504         ljmp setup_stall
507 setup_bmreq_is_standard:        
508         cjne r2, #0x00, setup_breq_not_00
509         ;; 00:  Get_Status (sub-switch on bmRequestType: device, ep, int)
510         cjne r1, #0x80, setup_Get_Status_not_device
511         ;; Get_Status(device)
512         ;;  are we self-powered? no. can we do remote wakeup? no
513         ;;   so return two zero bytes. This is reusable
514 setup_return_two_zero_bytes:
515         mov dptr, IN0BUF
516         clr a
517         movx @dptr, a
518         inc dptr
519         movx @dptr, a
520         mov dptr, IN0BC
521         mov a, #2
522         movx @dptr, a
523         ljmp setup_done_ack
524 setup_Get_Status_not_device:
525         cjne r1, #0x82, setup_Get_Status_not_endpoint
526         ;; Get_Status(endpoint)
527         ;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
528         ;; for now: cheat. TODO
529         sjmp setup_return_two_zero_bytes
530 setup_Get_Status_not_endpoint:
531         cjne r1, #0x81, setup_Get_Status_not_interface
532         ;; Get_Status(interface): return two zeros
533         sjmp setup_return_two_zero_bytes
534 setup_Get_Status_not_interface: 
535         ljmp setup_stall
536         
537 setup_breq_not_00:
538         cjne r2, #0x01, setup_breq_not_01
539         ;; 01:  Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
540         cjne r3, #0x00, setup_Clear_Feature_not_stall
541         ;; Clear_Feature(stall). should clear a stall bit. TODO
542         ljmp setup_stall
543 setup_Clear_Feature_not_stall:
544         cjne r3, #0x01, setup_Clear_Feature_not_rwake
545         ;; Clear_Feature(remote wakeup). ignored.
546         ljmp setup_done_ack
547 setup_Clear_Feature_not_rwake:
548         ljmp setup_stall
549         
550 setup_breq_not_01:
551         cjne r2, #0x03, setup_breq_not_03
552         ;; 03:  Set_Feature (sub-switch on wValueL: stall, remote wakeup)
553         cjne r3, #0x00, setup_Set_Feature_not_stall
554         ;; Set_Feature(stall). Should set a stall bit. TODO
555         ljmp setup_stall
556 setup_Set_Feature_not_stall:
557         cjne r3, #0x01, setup_Set_Feature_not_rwake
558         ;; Set_Feature(remote wakeup). ignored.
559         ljmp setup_done_ack
560 setup_Set_Feature_not_rwake:
561         ljmp setup_stall
562         
563 setup_breq_not_03:      
564         cjne r2, #0x06, setup_breq_not_06
565         ;; 06:  Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
566         cjne r4, #0x01, setup_Get_Descriptor_not_device
567         ;; Get_Descriptor(device)
568         mov dptr, SUDPTRH
569         mov a, #HIGH(desc_device)
570         movx @dptr, a
571         mov dptr, SUDPTRL
572         mov a, #LOW(desc_device)
573         movx @dptr, a
574         ljmp setup_done_ack
575 setup_Get_Descriptor_not_device:
576         cjne r4, #0x02, setup_Get_Descriptor_not_config
577         ;; Get_Descriptor(config[n])
578         cjne r3, #0x00, setup_stall; only handle n==0
579         ;; Get_Descriptor(config[0])
580         mov dptr, SUDPTRH
581         mov a, #HIGH(desc_config1)
582         movx @dptr, a
583         mov dptr, SUDPTRL
584         mov a, #LOW(desc_config1)
585         movx @dptr, a
586         ljmp setup_done_ack
587 setup_Get_Descriptor_not_config:
588         cjne r4, #0x03, setup_Get_Descriptor_not_string
589         ;; Get_Descriptor(string[wValueL])
590         ;;  if (wValueL >= maxstrings) stall
591         mov a, #((desc_strings_end-desc_strings)/2)
592         clr c
593         subb a,r3               ; a=4, r3 = 0..3 . if a<=0 then stall
594         jc  setup_stall
595         jz  setup_stall
596         mov a, r3
597         add a, r3               ; a = 2*wValueL
598         mov dptr, #desc_strings
599         add a, dpl
600         mov dpl, a
601         mov a, #0
602         addc a, dph
603         mov dph, a              ; dph = desc_strings[a]. big endian! (handy)
604         ;; it looks like my adapter uses a revision of the EZUSB that
605         ;; contains "rev D errata number 8", as hinted in the EzUSB example
606         ;; code. I cannot find an actual errata description on the Cypress
607         ;; web site, but from the example code it looks like this bug causes
608         ;; the length of string descriptors to be read incorrectly, possibly
609         ;; sending back more characters than the descriptor has. The workaround
610         ;; is to manually send out all of the data. The consequence of not
611         ;; using the workaround is that the strings gathered by the kernel
612         ;; driver are too long and are filled with trailing garbage (including
613         ;; leftover strings). Writing this out by hand is a nuisance, so for
614         ;; now I will just live with the bug.
615         movx a, @dptr
616         mov r1, a
617         inc dptr
618         movx a, @dptr
619         mov r2, a
620         mov dptr, SUDPTRH
621         mov a, r1
622         movx @dptr, a
623         mov dptr, SUDPTRL
624         mov a, r2
625         movx @dptr, a
626         ;; done
627         ljmp setup_done_ack
628         
629 setup_Get_Descriptor_not_string:
630         ljmp setup_stall
631         
632 setup_breq_not_06:
633         cjne r2, #0x08, setup_breq_not_08
634         ;; Get_Configuration. always 1. return one byte.
635         ;; this is reusable
636         mov a, #1
637 setup_return_one_byte:  
638         mov dptr, IN0BUF
639         movx @dptr, a
640         mov a, #1
641         mov dptr, IN0BC
642         movx @dptr, a
643         ljmp setup_done_ack
644 setup_breq_not_08:
645         cjne r2, #0x09, setup_breq_not_09
646         ;; 09: Set_Configuration. ignored.
647         ljmp setup_done_ack
648 setup_breq_not_09:
649         cjne r2, #0x0a, setup_breq_not_0a
650         ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
651         ;;  since we only have one interface, ignore wIndexL, return a 0
652         mov a, #0
653         ljmp setup_return_one_byte
654 setup_breq_not_0a:
655         cjne r2, #0x0b, setup_breq_not_0b
656         ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
657         ljmp setup_done_ack
658 setup_breq_not_0b:
659         ljmp setup_stall
661                 
662 setup_done_ack: 
663         ;; now clear HSNAK
664         mov dptr, EP0CS
665         mov a, #0x02
666         movx @dptr, a
667         sjmp setup_done
668 setup_stall:    
669         ;; unhandled. STALL
670         ;EP0CS |= bmEPSTALL
671         mov dptr, EP0CS
672         movx a, @dptr
673         orl a, EP0STALLbit
674         movx @dptr, a
675         sjmp setup_done
676         
677 setup_done:     
678         pop acc
679         pop dph1
680         pop dpl1
681         pop dph
682         pop dpl
683         pop dps
684         reti
686 ;;; ==============================================================
687         
688 set_baud:                       ; baud index in r3
689         ;; verify a < 10
690         mov a, r3
691         jb ACC.7, set_baud__badbaud
692         clr c
693         subb a, #10
694         jnc set_baud__badbaud
695         mov a, r3
696         rl a                    ; a = index*2
697         add a, #LOW(baud_table)
698         mov dpl, a
699         mov a, #HIGH(baud_table)
700         addc a, #0
701         mov dph, a
702         ;; TODO: shut down xmit/receive
703         ;; TODO: wait for current xmit char to leave
704         ;; TODO: shut down timer to avoid partial-char glitch
705         movx a,@dptr            ; BAUD_HIGH
706         mov RCAP2H, a
707         mov TH2, a
708         inc dptr
709         movx a,@dptr            ; BAUD_LOW
710         mov RCAP2L, a
711         mov TL2, a
712         ;; TODO: restart xmit/receive
713         ;; TODO: reenable interrupts, resume tx if pending
714         clr c                   ; c=0: success
715         ret
716 set_baud__badbaud:
717         setb c                  ; c=1: failure
718         ret
719         
720 ;;; ==================================================
721 control_pins:
722         cjne r1, #0x41, control_pins_in
723 control_pins_out:
724                 ;TODO BKPT is DTR
725         mov a, r3 ; wValue[0] holds new bits:   b7 is new RTS
726         xrl a, #0xff            ; 1 means active, 0V, +12V ?
727         anl a, #0x80
728         mov r3, a
729         mov dptr, OUTC
730         movx a, @dptr           ; only change bit 7 
731         anl a, #0x7F            ; ~0x84
732         orl a, r3
733         movx @dptr, a           ; other pins are inputs, bits ignored
734         ljmp setup_done_ack
735 control_pins_in:
736         mov dptr, PINSC
737         movx a, @dptr
738         xrl a, #0xff
739         ljmp setup_return_one_byte
741 ;;; ========================================
742         
743 ISR_Ep2in:
744         push dps
745         push dpl
746         push dph
747         push dpl1
748         push dph1
749         push acc
750         mov a,EXIF
751         clr acc.4
752         mov EXIF,a              ; clear INT2 first
753         mov dptr, IN07IRQ       ; clear USB int
754         mov a,#04h
755         movx @dptr,a
757         mov a, #0x20             ; Turn off the green LED
758         mov dptr,OEA
759         movx @dptr, a
762         ;; do stuff
763         lcall start_in
765         mov a, #0x20             ; Turn off the green LED
766         mov dptr,OEA
767         movx @dptr, a
770         
771         pop acc
772         pop dph1
773         pop dpl1
774         pop dph
775         pop dpl
776         pop dps
777         reti
779 ISR_Ep2out:
780         push dps
781         push dpl
782         push dph
783         push dpl1
784         push dph1
785         push acc
787         mov a, #0x10             ; Turn the green LED
788         mov dptr,OEA
789         movx @dptr, a
793         mov a,EXIF
794         clr acc.4
795         mov EXIF,a              ; clear INT2 first
796         mov dptr, OUT07IRQ      ; clear USB int
797         mov a,#04h
798         movx @dptr,a
800         ;; do stuff
802         ;; copy data into buffer. for now, assume we will have enough space
803         mov dptr, OUT2BC        ; get byte count
804         movx a,@dptr
805         mov r1, a
806         clr a
807         mov dps, a
808         mov dptr, OUT2BUF       ; load DPTR0 with source
809         mov dph1, #HIGH(tx_ring)        ; load DPTR1 with target
810         mov dpl1, tx_ring_in
811 OUT_loop:
812         movx a,@dptr            ; read
813         inc dps                 ; switch to DPTR1: target
814         inc dpl1                ; target = tx_ring_in+1
815         movx @dptr,a            ; store
816         mov a,dpl1
817         cjne a, tx_ring_out, OUT_no_overflow
818         sjmp OUT_overflow
819 OUT_no_overflow:        
820         inc tx_ring_in          ; tx_ring_in++
821         inc dps                 ; switch to DPTR0: source
822         inc dptr
823         djnz r1, OUT_loop
824         sjmp OUT_done
825 OUT_overflow:
826         ;; signal overflow
827         ;; fall through
828 OUT_done:       
829         ;; ack
830         mov dptr,OUT2BC
831         movx @dptr,a
833         ;; start tx
834         acall maybe_start_tx
835         ;acall dump_stat
837         mov a, #0x20             ; Turn off the green LED
838         mov dptr,OEA
839         movx @dptr, a
840         
841         pop acc
842         pop dph1
843         pop dpl1
844         pop dph
845         pop dpl
846         pop dps
847         reti
849 dump_stat:
850         ;; fill in EP4in with a debugging message:
851         ;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
852         ;;   tx_active
853         ;;   tx_ring[0..15]
854         ;;   0xfc
855         ;;   rx_ring[0..15]
856         clr a
857         mov dps, a
858         
859         mov dptr, IN4CS
860         movx a, @dptr
861         jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
862         mov dptr, IN4BUF
863         
864         mov a, tx_ring_in
865         movx @dptr, a
866         inc dptr
867         mov a, tx_ring_out
868         movx @dptr, a
869         inc dptr
871         mov a, rx_ring_in
872         movx @dptr, a
873         inc dptr
874         mov a, rx_ring_out
875         movx @dptr, a
876         inc dptr
877         
878         clr a
879         jnb TX_RUNNING, dump_stat__no_tx_running
880         inc a
881 dump_stat__no_tx_running:
882         movx @dptr, a
883         inc dptr
884         ;; tx_ring[0..15]
885         inc dps
886         mov dptr, #tx_ring      ; DPTR1: source
887         mov r1, #16
888 dump_stat__tx_ring_loop:
889         movx a, @dptr
890         inc dptr
891         inc dps
892         movx @dptr, a
893         inc dptr
894         inc dps
895         djnz r1, dump_stat__tx_ring_loop
896         inc dps
897         
898         mov a, #0xfc
899         movx @dptr, a
900         inc dptr
901         
902         ;; rx_ring[0..15]
903         inc dps
904         mov dptr, #rx_ring      ; DPTR1: source
905         mov r1, #16
906 dump_stat__rx_ring_loop:
907         movx a, @dptr
908         inc dptr
909         inc dps
910         movx @dptr, a
911         inc dptr
912         inc dps
913         djnz r1, dump_stat__rx_ring_loop
914         
915         ;; now send it
916         clr a
917         mov dps, a
918         mov dptr, IN4BC
919         mov a, #38
920         movx @dptr, a
921 dump_stat__done:        
922         ret
923                 
924 ;;; ============================================================
925         
926 maybe_start_tx:
927         ;; make sure the tx process is running.
928         jb TX_RUNNING, start_tx_done
929 start_tx:
930         ;; is there work to be done?
931         mov a, tx_ring_in
932         cjne a,tx_ring_out, start_tx__work
933         ret                     ; no work
934 start_tx__work: 
935         ;; tx was not running. send the first character, setup the TI int
936         inc tx_ring_out         ; [++tx_ring_out]
937         mov dph, #HIGH(tx_ring)
938         mov dpl, tx_ring_out
939         movx a, @dptr
940         mov sbuf, a
941         setb TX_RUNNING
942 start_tx_done:
943         ;; can we unthrottle the host tx process?
944         ;;  step 1: do we care?
945         mov a, #0
946         cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
947         ;; nope
948 start_tx_really_done:
949         ret
950 start_tx__maybe_unthrottle_tx:
951         ;;  step 2: is there now room?
952         mov a, tx_ring_out
953         setb c
954         subb a, tx_ring_in
955         ;; a is now write_room. If thresh >= a, we can unthrottle
956         clr c
957         subb a, tx_unthrottle_threshold
958         jc start_tx_really_done ; nope
959         ;; yes, we can unthrottle. remove the threshold and mark a request
960         mov tx_unthrottle_threshold, #0
961         setb DO_TX_UNTHROTTLE
962         ;; prod rx, which will actually send the message when in2 becomes free
963         ljmp start_in
964         
966 serial_int:
967         push dps
968         push dpl
969         push dph
970         push dpl1
971         push dph1
972         push acc
973         jnb TI, serial_int__not_tx
974         ;; tx finished. send another character if we have one
975         clr TI                  ; clear int
976         clr TX_RUNNING
977         lcall start_tx
978 serial_int__not_tx:
979         jnb RI, serial_int__not_rx
980         lcall get_rx_char
981         clr RI                  ; clear int
982 serial_int__not_rx:     
983         ;; return
984         pop acc
985         pop dph1
986         pop dpl1
987         pop dph
988         pop dpl
989         pop dps
990         reti
992 get_rx_char:
993         mov dph, #HIGH(rx_ring)
994         mov dpl, rx_ring_in
995         inc dpl                 ; target = rx_ring_in+1
996         mov a, sbuf
997         movx @dptr, a
998         ;; check for overflow before incrementing rx_ring_in
999         mov a, dpl
1000         cjne a, rx_ring_out, get_rx_char__no_overflow
1001         ;; signal overflow
1002         ret
1003 get_rx_char__no_overflow:       
1004         inc rx_ring_in
1005         ;; kick off USB INpipe
1006         acall start_in
1007         ret
1009 start_in:
1010         ;; check if the inpipe is already running.
1011         mov  a,#0x10
1012         mov dptr, OEA
1013         movx @dptr,a
1015         mov dptr, IN2CS
1016         movx a, @dptr
1017         jb acc.1, start_in__done; int will handle it
1018         jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
1019         ;; see if there is any work to do. a serial interrupt might occur
1020         ;; during this sequence?
1021         mov a, rx_ring_in
1022         cjne a, rx_ring_out, start_in__have_work
1023         ret                     ; nope
1024 start_in__have_work:    
1025         ;; now copy as much data as possible into the pipe. 63 bytes max.
1026         clr a
1027         mov dps, a
1028         mov dph, #HIGH(rx_ring) ; load DPTR0 with source
1029         inc dps
1030         mov dptr, IN2BUF        ; load DPTR1 with target
1031         movx @dptr, a           ; in[0] signals that rest of IN is rx data
1032         inc dptr
1033         inc dps
1034         ;; loop until we run out of data, or we have copied 64 bytes
1035         mov r1, #1              ; INbuf size counter
1036 start_in__loop:
1037         mov a, rx_ring_in
1038         cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
1039         sjmp start_in__kick
1040 start_inlocal_irq_enablell_copying:
1041         inc rx_ring_out
1042         mov dpl, rx_ring_out
1043         movx a, @dptr
1044         inc dps
1045         movx @dptr, a           ; write into IN buffer
1046         inc dptr
1047         inc dps
1048         inc r1
1049         cjne r1, #64, start_in__loop; loop
1050 start_in__kick:
1051         ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
1052         ;; kick off IN
1053         mov a, #0x10             ; Turn the green LED
1054         mov dptr,OEA
1055         movx @dptr, a
1056         mov dptr, IN2BC
1057         mov a, r1
1058         jz start_in__done
1059         movx @dptr, a
1060         ;; done
1061 start_in__done:
1062         ;acall dump_stat
1063         ret
1064 start_in__do_tx_unthrottle:
1065         ;; special sequence: send a tx unthrottle message
1066         clr DO_TX_UNTHROTTLE
1067         clr a
1068         mov dps, a
1069         mov dptr, IN2BUF
1070         mov a, #1
1071         movx @dptr, a
1072         inc dptr
1073         mov a, #2
1074         movx @dptr, a
1075         mov dptr, IN2BC
1076         movx @dptr, a
1077         ret
1078         
1079 putchar:
1080         clr TI
1081         mov SBUF, a
1082 putchar_wait:
1083         jnb TI, putchar_wait
1084         clr TI
1085         ret
1087         
1088 baud_table:                     ; baud_high, then baud_low
1089         ;; baud[0]: 110
1090         .byte BAUD_HIGH(110)
1091         .byte BAUD_LOW(110)
1092         ;; baud[1]: 300
1093         .byte BAUD_HIGH(300)
1094         .byte BAUD_LOW(300)
1095         ;; baud[2]: 1200
1096         .byte BAUD_HIGH(1200)
1097         .byte BAUD_LOW(1200)
1098         ;; baud[3]: 2400
1099         .byte BAUD_HIGH(2400)
1100         .byte BAUD_LOW(2400)
1101         ;; baud[4]: 4800
1102         .byte BAUD_HIGH(4800)
1103         .byte BAUD_LOW(4800)
1104         ;; baud[5]: 9600
1105         .byte BAUD_HIGH(9600)
1106         .byte BAUD_LOW(9600)
1107         ;; baud[6]: 19200
1108         .byte BAUD_HIGH(19200)
1109         .byte BAUD_LOW(19200)
1110         ;; baud[7]: 38400
1111         .byte BAUD_HIGH(38400)
1112         .byte BAUD_LOW(38400)
1113         ;; baud[8]: 57600
1114         .byte BAUD_HIGH(57600)
1115         .byte BAUD_LOW(57600)
1116         ;; baud[9]: 115200
1117         .byte BAUD_HIGH(115200)
1118         .byte BAUD_LOW(115200)
1120 desc_device:
1121         .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
1122         .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
1123 ;;; The "real" device id, which must match the host driver, is that
1124 ;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1125         
1126 desc_config1:
1127         .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1128         .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1129         .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1130         .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1132 desc_strings:
1133         .word string_langids, string_mfg, string_product, string_serial
1134 desc_strings_end:
1136 string_langids: .byte string_langids_end-string_langids
1137         .byte 3
1138         .word 0
1139 string_langids_end:
1141         ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
1142         ;; *that* is a pain in the ass to encode. And they are little-endian
1143         ;; too. Use this perl snippet to get the bytecodes:
1144         /* while (<>) {
1145             @c = split(//);
1146             foreach $c (@c) {
1147              printf("0x%02x, 0x00, ", ord($c));
1148             }
1149            }
1150         */
1152 string_mfg:     .byte string_mfg_end-string_mfg
1153         .byte 3
1154 ;       .byte "ACME usb widgets"
1155         .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
1156 string_mfg_end:
1157         
1158 string_product: .byte string_product_end-string_product
1159         .byte 3
1160 ;       .byte "ACME USB serial widget"
1161         .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
1162 string_product_end:
1163         
1164 string_serial:  .byte string_serial_end-string_serial
1165         .byte 3
1166 ;       .byte "47"
1167         .byte 0x34, 0x00, 0x37, 0x00
1168 string_serial_end:
1169                 
1170 ;;; ring buffer memory
1171         ;; tx_ring_in+1 is where the next input byte will go
1172         ;; [tx_ring_out] has been sent
1173         ;; if tx_ring_in == tx_ring_out, theres no work to do
1174         ;; there are (tx_ring_in - tx_ring_out) chars to be written
1175         ;; dont let _in lap _out
1176         ;;   cannot inc if tx_ring_in+1 == tx_ring_out
1177         ;;  write [tx_ring_in+1] then tx_ring_in++
1178         ;;   if (tx_ring_in+1 == tx_ring_out), overflow
1179         ;;   else tx_ring_in++
1180         ;;  read/send [tx_ring_out+1], then tx_ring_out++
1182         ;; rx_ring_in works the same way
1183         
1184         .org 0x1000
1185 tx_ring:
1186         .skip 0x100             ; 256 bytes
1187 rx_ring:
1188         .skip 0x100             ; 256 bytes
1189         
1190         
1191         .END
1192