Setting up repository
[linux-libre-firmware.git] / keyspan_pda / keyspan_pda.S
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