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