aboutsummaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2008-05-30 08:15:13 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2008-07-10 09:49:09 -0400
commit3edbf98b863391bdd7ad2bf47b7db1689afac886 (patch)
tree14d36c188adb20790f61cb046f7e95b01000bcbe /firmware
parent2971c579f93bcff26744672ea98c13bef71ded97 (diff)
keyspan_pda: use request_firmware()
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'firmware')
-rw-r--r--firmware/Makefile2
-rw-r--r--firmware/WHENCE14
-rw-r--r--firmware/keyspan_pda/keyspan_pda.HEX83
-rw-r--r--firmware/keyspan_pda/keyspan_pda.S1124
-rw-r--r--firmware/keyspan_pda/xircom_pgs.HEX87
-rw-r--r--firmware/keyspan_pda/xircom_pgs.S1192
6 files changed, 2502 insertions, 0 deletions
diff --git a/firmware/Makefile b/firmware/Makefile
index db1b01a7a668..dd76fa5ab3fc 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -49,6 +49,8 @@ fw-shipped- := keyspan/mpr.fw keyspan/usa18x.fw keyspan/usa19.fw \
49 keyspan/usa28.fw keyspan/usa28xa.fw keyspan/usa28xb.fw \ 49 keyspan/usa28.fw keyspan/usa28xa.fw keyspan/usa28xb.fw \
50 keyspan/usa28x.fw keyspan/usa49w.fw keyspan/usa49wlc.fw 50 keyspan/usa28x.fw keyspan/usa49w.fw keyspan/usa49wlc.fw
51endif 51endif
52fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda/keyspan_pda.fw
53fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw
52 54
53fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-) 55fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
54 56
diff --git a/firmware/WHENCE b/firmware/WHENCE
index d66291a78f07..b5be4bc88007 100644
--- a/firmware/WHENCE
+++ b/firmware/WHENCE
@@ -119,3 +119,17 @@ Original licence information:
119 part, requires the inclusion of this statement." 119 part, requires the inclusion of this statement."
120 120
121-------------------------------------------------------------------------- 121--------------------------------------------------------------------------
122
123Driver: keyspan_pda -- USB Keyspan PDA single-port serial device
124
125File: keyspan_pda/keyspan_pda.fw
126Source: keyspan_pda/keyspan_pda.S
127
128File: keyspan_pda/xircom_pgs.fw
129Source: keyspan_pda/xircom_pgs.S
130
131Licence: GPLv2+
132
133Compiled from original 8051 source into Intel HEX, used in our binary ihex form.
134
135--------------------------------------------------------------------------
diff --git a/firmware/keyspan_pda/keyspan_pda.HEX b/firmware/keyspan_pda/keyspan_pda.HEX
new file mode 100644
index 000000000000..6fcf02bb4b2d
--- /dev/null
+++ b/firmware/keyspan_pda/keyspan_pda.HEX
@@ -0,0 +1,83 @@
1:03000000020200F9
2:0400230002055F0073
3:0400430002010000B6
4:050030000000000000CB
5:10010000020296000200000002000000020000004F
6:1001100002000000020000000200000002000000D7
7:1001200002000000020000000204610002048900D5
8:1002000075815EE4F532F533F530F531F534C20031
9:10021000C201A90074FE901000F0A3D9FC74FD90F7
10:100220001100F0A3D9FC7402907F9DF07400907FC0
11:1002300097F07486907F9EF0907F957403F0907F86
12:10024000AFE0D2E0F07401907FABF0907FAEF09021
13:100250007FAC7404F0907FAD7404F0907FC9F074AB
14:1002600084907F98F07400F59875C8307B059120D4
15:10027000D2CA759850D2E8D2AFD2AC7400F586904D
16:100280007FD67402F0792E7A007B00DBFEDAFAD991
17:10029000F67406F080FEC086C082C083C084C0852C
18:1002A000C0E0E591C2E4F591907FAB7401F0907FDE
19:1002B000E8E0F9A3E0FAA3E0FBA3E0FCE95460B4B2
20:1002C0000003020339B4406EBA000B12042040034D
21:1002D00002040202040ABA010302040ABA02030277
22:1002E000040ABA0303020444BA041EBB000A907F46
23:1002F00095E04402F0020402907F98E054FDF090F3
24:100300007F95E054FDF0020402BA050302040ABA24
25:100310000619BB0008E533D395320203DEBB0108A2
26:10032000E532C395330203DE02040ABA07058B34B3
27:1003300002040202040A02040ABA0020B9801090E2
28:100340007F00E4F0A3F0907FB57402F0020402B9DC
29:10035000820280EBB9810280E602040ABA010FBB77
30:10036000000302040ABB010302040202040ABA03E6
31:100370000FBB000302040ABB010302040202040AC9
32:10038000BA0656BC010F907FD47406F0907FD574E6
33:1003900012F0020402BC0212BB006F907FD47406FC
34:1003A000F0907FD57424F0020402BC03297404C3C6
35:1003B0009B40576055EB2B9006442582F5827400D4
36:1003C0003583F583E0F9A3E0FA907FD4E9F0907FDC
37:1003D000D5EAF002040202040ABA080F7401907F01
38:1003E00000F07401907FB5F0020402BA0903020420
39:1003F00002BA0A0574000203DEBA0B030204020209
40:10040000040A907FB47402F08009907FB4E0440144
41:10041000F08000D0E0D085D084D083D082D08632E6
42:10042000EB20E71EC3940A5019EB2324FEF58274D7
43:10043000053400F583E0F5CBF5CDA3E0F5CAF5CCA6
44:10044000C322D322B94111EB64FF5484FB907F98FF
45:10045000E0547B4BF0020402907F9BE064FF0203B8
46:10046000DEC086C082C083C084C085C0E0E591C282
47:10047000E4F591907FA97404F01205A0D0E0D08536
48:10048000D084D083D082D08632C086C082C083C060
49:1004900084C085C0E0E591C2E4F591907FAA740420
50:1004A000F0907FC9E0F9E4F586907DC075851085F0
51:1004B0003284E005860584F0E584B53302800905C1
52:1004C000320586A3D9EC8000907FC9F0B131D0E02D
53:1004D000D085D084D083D082D08632E4F586907FD8
54:1004E000BCE020E14B907D00E532F0A3E533F0A3C2
55:1004F000E530F0A3E531F0A3E430000104F0A305FA
56:10050000869010007910E0A30586F0A30586D9F641
57:10051000058674FCF0A305869011007910E0A30510
58:1005200086F0A30586D9F6E4F586907FBD7426F0A3
59:1005300022200013E532B53301220533758310857F
60:100540003382E0F599D2007400B5340122E533D34B
61:100550009532C3953440F5753400D2010205A0C030
62:1005600086C082C083C084C085C0E0309907C2992C
63:10057000C20012053430980512058AC298D0E0D026
64:1005800085D084D083D082D0863275831185308225
65:100590000582E599F0E582B53101220530B1A0224E
66:1005A000907FB8E020E138200136E530B5310122F6
67:1005B000E4F5867583110586907E00F0A3058679A3
68:1005C00001E530B5310280100531853182E00586C4
69:1005D000F0A3058609B940E9907FB9E96001F022EE
70:1005E000C201E4F586907E007401F0A37402F090DD
71:1005F0007FB9F022C299F5993099FDC29922E55E42
72:10060000F63CFD8FFEC8FF64FFB2FFD9FFEDFFF39C
73:10061000FFFA12010001FFFFFF40CD06040189AB84
74:1006200001020301090220000101008032090400D7
75:100630000002FFFFFF0007058203400001070502DB
76:1006400002400000064C0650067206A0040300009B
77:100650002203410043004D00450020007500730057
78:100660006200200077006900640067006500740084
79:1006700073002E03410043004D004500200055004B
80:1006800053004200200073006500720069006100A1
81:100690006C0020007700690064006700650074004A
82:0606A000060334003700E0
83:00000001FF
diff --git a/firmware/keyspan_pda/keyspan_pda.S b/firmware/keyspan_pda/keyspan_pda.S
new file mode 100644
index 000000000000..418fe69aa5e0
--- /dev/null
+++ b/firmware/keyspan_pda/keyspan_pda.S
@@ -0,0 +1,1124 @@
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
181rx_ring_in: .byte 0
182rx_ring_out: .byte 0
183tx_ring_in: .byte 0
184tx_ring_out: .byte 0
185tx_unthrottle_threshold: .byte 0
186
187 .org 0x100H ; wants to be on a page boundary
188USB_Jump_Table:
189 ljmp ISR_Sudav ; Setup Data Available
190 .byte 0
191 ljmp 0 ; Start of Frame
192 .byte 0
193 ljmp 0 ; Setup Data Loading
194 .byte 0
195 ljmp 0 ; Global Suspend
196 .byte 0
197 ljmp 0 ; USB Reset
198 .byte 0
199 ljmp 0 ; Reserved
200 .byte 0
201 ljmp 0 ; End Point 0 In
202 .byte 0
203 ljmp 0 ; End Point 0 Out
204 .byte 0
205 ljmp 0 ; End Point 1 In
206 .byte 0
207 ljmp 0 ; End Point 1 Out
208 .byte 0
209 ljmp ISR_Ep2in
210 .byte 0
211 ljmp ISR_Ep2out
212 .byte 0
213
214
215 .org 0x200
216
217start: mov SP,STACK-1 ; set stack
218 ;; clear local variables
219 clr a
220 mov tx_ring_in, a
221 mov tx_ring_out, a
222 mov rx_ring_in, a
223 mov rx_ring_out, a
224 mov tx_unthrottle_threshold, a
225 clr TX_RUNNING
226 clr DO_TX_UNTHROTTLE
227
228 ;; clear fifo with "fe"
229 mov r1, 0
230 mov a, #0xfe
231 mov dptr, #tx_ring
232clear_tx_ring_loop:
233 movx @dptr, a
234 inc dptr
235 djnz r1, clear_tx_ring_loop
236
237 mov a, #0xfd
238 mov dptr, #rx_ring
239clear_rx_ring_loop:
240 movx @dptr, a
241 inc dptr
242 djnz r1, clear_rx_ring_loop
243
244;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
245 ;; set OEB.1
246 mov a, #02H
247 mov dptr,OEB
248 movx @dptr,a
249 ;; clear PB1
250 mov a, #00H
251 mov dptr,OUTB
252 movx @dptr,a
253 ;; set OEC.[127]
254 mov a, #0x86
255 mov dptr,OEC
256 movx @dptr,a
257 ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
258 mov dptr, PORTCCFG
259 mov a, #0x03
260 movx @dptr, a
261
262 ;; set up interrupts, autovectoring
263 mov dptr, USBBAV
264 movx a,@dptr
265 setb acc.0 ; AVEN bit to 0
266 movx @dptr, a
267
268 mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
269 mov dptr, USBIRQ
270 movx @dptr, a ; clear SUDAVI
271 mov dptr, USBIEN
272 movx @dptr, a
273
274 mov dptr, IN07IEN
275 mov a,#0x04 ; enable IN2 int
276 movx @dptr, a
277
278 mov dptr, OUT07IEN
279 mov a,#0x04 ; enable OUT2 int
280 movx @dptr, a
281 mov dptr, OUT2BC
282 movx @dptr, a ; arm OUT2
283
284 mov a, #0x84 ; turn on RTS, DTR
285 mov dptr,OUTC
286 movx @dptr, a
287 ;; setup the serial port. 9600 8N1.
288 mov a,#01010011 ; mode 1, enable rx, clear int
289 mov SCON, a
290 ;; using timer2, in 16-bit baud-rate-generator mode
291 ;; (xtal 12MHz, internal fosc 24MHz)
292 ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
293 ;; 57600: 0xFFF2.F, say 0xFFF3
294 ;; 9600: 0xFFB1.E, say 0xFFB2
295 ;; 300: 0xF63C
296#define BAUD 9600
297#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
298#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
299#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
300
301 mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
302 mov r3, #5
303 acall set_baud
304 setb TR2
305 mov SCON, #050h
306
307#if 0
308 mov r1, #0x40
309 mov a, #0x41
310send:
311 mov SBUF, a
312 inc a
313 anl a, #0x3F
314 orl a, #0x40
315; xrl a, #0x02
316wait1:
317 jnb TI, wait1
318 clr TI
319 djnz r1, send
320;done: sjmp done
321
322#endif
323
324 setb EUSB
325 setb EA
326 setb ES0
327 ;acall dump_stat
328
329 ;; hey, what say we RENUMERATE! (TRM p.62)
330 mov a, #0
331 mov dps, a
332 mov dptr, USBCS
333 mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
334 movx @dptr, a
335 ;; now presence pin is floating, simulating disconnect. wait 0.5s
336 mov r1, #46
337renum_wait1:
338 mov r2, #0
339renum_wait2:
340 mov r3, #0
341renum_wait3:
342 djnz r3, renum_wait3
343 djnz r2, renum_wait2
344 djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
345 mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
346 movx @dptr, a
347 ;; we are back online. the host device will now re-query us
348
349
350main: sjmp main
351
352
353
354ISR_Sudav:
355 push dps
356 push dpl
357 push dph
358 push dpl1
359 push dph1
360 push acc
361 mov a,EXIF
362 clr acc.4
363 mov EXIF,a ; clear INT2 first
364 mov dptr, USBIRQ ; clear USB int
365 mov a,#01h
366 movx @dptr,a
367
368 ;; get request type
369 mov dptr, SETUPDAT
370 movx a, @dptr
371 mov r1, a ; r1 = bmRequestType
372 inc dptr
373 movx a, @dptr
374 mov r2, a ; r2 = bRequest
375 inc dptr
376 movx a, @dptr
377 mov r3, a ; r3 = wValueL
378 inc dptr
379 movx a, @dptr
380 mov r4, a ; r4 = wValueH
381
382 ;; main switch on bmRequest.type: standard or vendor
383 mov a, r1
384 anl a, #0x60
385 cjne a, #0x00, setup_bmreq_type_not_standard
386 ;; standard request: now main switch is on bRequest
387 ljmp setup_bmreq_is_standard
388
389setup_bmreq_type_not_standard:
390 ;; a still has bmreq&0x60
391 cjne a, #0x40, setup_bmreq_type_not_vendor
392 ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
393 ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
394 cjne r2, #0x00, setup_ctrl_not_00
395 ;; 00 is set baud, wValue[0] has baud rate index
396 lcall set_baud ; index in r3, carry set if error
397 jc setup_bmreq_type_not_standard__do_stall
398 ljmp setup_done_ack
399setup_bmreq_type_not_standard__do_stall:
400 ljmp setup_stall
401setup_ctrl_not_00:
402 cjne r2, #0x01, setup_ctrl_not_01
403 ;; 01 is reserved for set bits (parity). TODO
404 ljmp setup_stall
405setup_ctrl_not_01:
406 cjne r2, #0x02, setup_ctrl_not_02
407 ;; 02 is set HW flow control. TODO
408 ljmp setup_stall
409setup_ctrl_not_02:
410 cjne r2, #0x03, setup_ctrl_not_03
411 ;; 03 is control pins (RTS, DTR).
412 ljmp control_pins ; will jump to setup_done_ack,
413 ; or setup_return_one_byte
414setup_ctrl_not_03:
415 cjne r2, #0x04, setup_ctrl_not_04
416 ;; 04 is send break (really "turn break on/off"). TODO
417 cjne r3, #0x00, setup_ctrl_do_break_on
418 ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
419 mov dptr, PORTCCFG
420 movx a, @dptr
421 orl a, #0x02
422 movx @dptr, a
423 ljmp setup_done_ack
424setup_ctrl_do_break_on:
425 ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
426 mov dptr, OUTC
427 movx a, @dptr
428 anl a, #0xfd ; ~0x02
429 movx @dptr, a
430 mov dptr, PORTCCFG
431 movx a, @dptr
432 anl a, #0xfd ; ~0x02
433 movx @dptr, a
434 ljmp setup_done_ack
435setup_ctrl_not_04:
436 cjne r2, #0x05, setup_ctrl_not_05
437 ;; 05 is set desired interrupt bitmap. TODO
438 ljmp setup_stall
439setup_ctrl_not_05:
440 cjne r2, #0x06, setup_ctrl_not_06
441 ;; 06 is query room
442 cjne r3, #0x00, setup_ctrl_06_not_00
443 ;; 06, wValue[0]=0 is query write_room
444 mov a, tx_ring_out
445 setb c
446 subb a, tx_ring_in ; out-1-in = 255 - (in-out)
447 ljmp setup_return_one_byte
448setup_ctrl_06_not_00:
449 cjne r3, #0x01, setup_ctrl_06_not_01
450 ;; 06, wValue[0]=1 is query chars_in_buffer
451 mov a, tx_ring_in
452 clr c
453 subb a, tx_ring_out ; in-out
454 ljmp setup_return_one_byte
455setup_ctrl_06_not_01:
456 ljmp setup_stall
457setup_ctrl_not_06:
458 cjne r2, #0x07, setup_ctrl_not_07
459 ;; 07 is request tx unthrottle interrupt
460 mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
461 ljmp setup_done_ack
462setup_ctrl_not_07:
463 ljmp setup_stall
464
465setup_bmreq_type_not_vendor:
466 ljmp setup_stall
467
468
469setup_bmreq_is_standard:
470 cjne r2, #0x00, setup_breq_not_00
471 ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
472 cjne r1, #0x80, setup_Get_Status_not_device
473 ;; Get_Status(device)
474 ;; are we self-powered? no. can we do remote wakeup? no
475 ;; so return two zero bytes. This is reusable
476setup_return_two_zero_bytes:
477 mov dptr, IN0BUF
478 clr a
479 movx @dptr, a
480 inc dptr
481 movx @dptr, a
482 mov dptr, IN0BC
483 mov a, #2
484 movx @dptr, a
485 ljmp setup_done_ack
486setup_Get_Status_not_device:
487 cjne r1, #0x82, setup_Get_Status_not_endpoint
488 ;; Get_Status(endpoint)
489 ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
490 ;; for now: cheat. TODO
491 sjmp setup_return_two_zero_bytes
492setup_Get_Status_not_endpoint:
493 cjne r1, #0x81, setup_Get_Status_not_interface
494 ;; Get_Status(interface): return two zeros
495 sjmp setup_return_two_zero_bytes
496setup_Get_Status_not_interface:
497 ljmp setup_stall
498
499setup_breq_not_00:
500 cjne r2, #0x01, setup_breq_not_01
501 ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
502 cjne r3, #0x00, setup_Clear_Feature_not_stall
503 ;; Clear_Feature(stall). should clear a stall bit. TODO
504 ljmp setup_stall
505setup_Clear_Feature_not_stall:
506 cjne r3, #0x01, setup_Clear_Feature_not_rwake
507 ;; Clear_Feature(remote wakeup). ignored.
508 ljmp setup_done_ack
509setup_Clear_Feature_not_rwake:
510 ljmp setup_stall
511
512setup_breq_not_01:
513 cjne r2, #0x03, setup_breq_not_03
514 ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
515 cjne r3, #0x00, setup_Set_Feature_not_stall
516 ;; Set_Feature(stall). Should set a stall bit. TODO
517 ljmp setup_stall
518setup_Set_Feature_not_stall:
519 cjne r3, #0x01, setup_Set_Feature_not_rwake
520 ;; Set_Feature(remote wakeup). ignored.
521 ljmp setup_done_ack
522setup_Set_Feature_not_rwake:
523 ljmp setup_stall
524
525setup_breq_not_03:
526 cjne r2, #0x06, setup_breq_not_06
527 ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
528 cjne r4, #0x01, setup_Get_Descriptor_not_device
529 ;; Get_Descriptor(device)
530 mov dptr, SUDPTRH
531 mov a, #HIGH(desc_device)
532 movx @dptr, a
533 mov dptr, SUDPTRL
534 mov a, #LOW(desc_device)
535 movx @dptr, a
536 ljmp setup_done_ack
537setup_Get_Descriptor_not_device:
538 cjne r4, #0x02, setup_Get_Descriptor_not_config
539 ;; Get_Descriptor(config[n])
540 cjne r3, #0x00, setup_stall; only handle n==0
541 ;; Get_Descriptor(config[0])
542 mov dptr, SUDPTRH
543 mov a, #HIGH(desc_config1)
544 movx @dptr, a
545 mov dptr, SUDPTRL
546 mov a, #LOW(desc_config1)
547 movx @dptr, a
548 ljmp setup_done_ack
549setup_Get_Descriptor_not_config:
550 cjne r4, #0x03, setup_Get_Descriptor_not_string
551 ;; Get_Descriptor(string[wValueL])
552 ;; if (wValueL >= maxstrings) stall
553 mov a, #((desc_strings_end-desc_strings)/2)
554 clr c
555 subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
556 jc setup_stall
557 jz setup_stall
558 mov a, r3
559 add a, r3 ; a = 2*wValueL
560 mov dptr, #desc_strings
561 add a, dpl
562 mov dpl, a
563 mov a, #0
564 addc a, dph
565 mov dph, a ; dph = desc_strings[a]. big endian! (handy)
566 ;; it looks like my adapter uses a revision of the EZUSB that
567 ;; contains "rev D errata number 8", as hinted in the EzUSB example
568 ;; code. I cannot find an actual errata description on the Cypress
569 ;; web site, but from the example code it looks like this bug causes
570 ;; the length of string descriptors to be read incorrectly, possibly
571 ;; sending back more characters than the descriptor has. The workaround
572 ;; is to manually send out all of the data. The consequence of not
573 ;; using the workaround is that the strings gathered by the kernel
574 ;; driver are too long and are filled with trailing garbage (including
575 ;; leftover strings). Writing this out by hand is a nuisance, so for
576 ;; now I will just live with the bug.
577 movx a, @dptr
578 mov r1, a
579 inc dptr
580 movx a, @dptr
581 mov r2, a
582 mov dptr, SUDPTRH
583 mov a, r1
584 movx @dptr, a
585 mov dptr, SUDPTRL
586 mov a, r2
587 movx @dptr, a
588 ;; done
589 ljmp setup_done_ack
590
591setup_Get_Descriptor_not_string:
592 ljmp setup_stall
593
594setup_breq_not_06:
595 cjne r2, #0x08, setup_breq_not_08
596 ;; Get_Configuration. always 1. return one byte.
597 ;; this is reusable
598 mov a, #1
599setup_return_one_byte:
600 mov dptr, IN0BUF
601 movx @dptr, a
602 mov a, #1
603 mov dptr, IN0BC
604 movx @dptr, a
605 ljmp setup_done_ack
606setup_breq_not_08:
607 cjne r2, #0x09, setup_breq_not_09
608 ;; 09: Set_Configuration. ignored.
609 ljmp setup_done_ack
610setup_breq_not_09:
611 cjne r2, #0x0a, setup_breq_not_0a
612 ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
613 ;; since we only have one interface, ignore wIndexL, return a 0
614 mov a, #0
615 ljmp setup_return_one_byte
616setup_breq_not_0a:
617 cjne r2, #0x0b, setup_breq_not_0b
618 ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
619 ljmp setup_done_ack
620setup_breq_not_0b:
621 ljmp setup_stall
622
623
624setup_done_ack:
625 ;; now clear HSNAK
626 mov dptr, EP0CS
627 mov a, #0x02
628 movx @dptr, a
629 sjmp setup_done
630setup_stall:
631 ;; unhandled. STALL
632 ;EP0CS |= bmEPSTALL
633 mov dptr, EP0CS
634 movx a, @dptr
635 orl a, EP0STALLbit
636 movx @dptr, a
637 sjmp setup_done
638
639setup_done:
640 pop acc
641 pop dph1
642 pop dpl1
643 pop dph
644 pop dpl
645 pop dps
646 reti
647
648;;; ==============================================================
649
650set_baud: ; baud index in r3
651 ;; verify a < 10
652 mov a, r3
653 jb ACC.7, set_baud__badbaud
654 clr c
655 subb a, #10
656 jnc set_baud__badbaud
657 mov a, r3
658 rl a ; a = index*2
659 add a, #LOW(baud_table)
660 mov dpl, a
661 mov a, #HIGH(baud_table)
662 addc a, #0
663 mov dph, a
664 ;; TODO: shut down xmit/receive
665 ;; TODO: wait for current xmit char to leave
666 ;; TODO: shut down timer to avoid partial-char glitch
667 movx a,@dptr ; BAUD_HIGH
668 mov RCAP2H, a
669 mov TH2, a
670 inc dptr
671 movx a,@dptr ; BAUD_LOW
672 mov RCAP2L, a
673 mov TL2, a
674 ;; TODO: restart xmit/receive
675 ;; TODO: reenable interrupts, resume tx if pending
676 clr c ; c=0: success
677 ret
678set_baud__badbaud:
679 setb c ; c=1: failure
680 ret
681
682;;; ==================================================
683control_pins:
684 cjne r1, #0x41, control_pins_in
685control_pins_out:
686 mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS
687 xrl a, #0xff ; 1 means active, 0V, +12V ?
688 anl a, #0x84
689 mov r3, a
690 mov dptr, OUTC
691 movx a, @dptr ; only change bits 7 and 2
692 anl a, #0x7b ; ~0x84
693 orl a, r3
694 movx @dptr, a ; other pins are inputs, bits ignored
695 ljmp setup_done_ack
696control_pins_in:
697 mov dptr, PINSC
698 movx a, @dptr
699 xrl a, #0xff
700 ljmp setup_return_one_byte
701
702;;; ========================================
703
704ISR_Ep2in:
705 push dps
706 push dpl
707 push dph
708 push dpl1
709 push dph1
710 push acc
711 mov a,EXIF
712 clr acc.4
713 mov EXIF,a ; clear INT2 first
714 mov dptr, IN07IRQ ; clear USB int
715 mov a,#04h
716 movx @dptr,a
717
718 ;; do stuff
719 lcall start_in
720
721 pop acc
722 pop dph1
723 pop dpl1
724 pop dph
725 pop dpl
726 pop dps
727 reti
728
729ISR_Ep2out:
730 push dps
731 push dpl
732 push dph
733 push dpl1
734 push dph1
735 push acc
736 mov a,EXIF
737 clr acc.4
738 mov EXIF,a ; clear INT2 first
739 mov dptr, OUT07IRQ ; clear USB int
740 mov a,#04h
741 movx @dptr,a
742
743 ;; do stuff
744
745 ;; copy data into buffer. for now, assume we will have enough space
746 mov dptr, OUT2BC ; get byte count
747 movx a,@dptr
748 mov r1, a
749 clr a
750 mov dps, a
751 mov dptr, OUT2BUF ; load DPTR0 with source
752 mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
753 mov dpl1, tx_ring_in
754OUT_loop:
755 movx a,@dptr ; read
756 inc dps ; switch to DPTR1: target
757 inc dpl1 ; target = tx_ring_in+1
758 movx @dptr,a ; store
759 mov a,dpl1
760 cjne a, tx_ring_out, OUT_no_overflow
761 sjmp OUT_overflow
762OUT_no_overflow:
763 inc tx_ring_in ; tx_ring_in++
764 inc dps ; switch to DPTR0: source
765 inc dptr
766 djnz r1, OUT_loop
767 sjmp OUT_done
768OUT_overflow:
769 ;; signal overflow
770 ;; fall through
771OUT_done:
772 ;; ack
773 mov dptr,OUT2BC
774 movx @dptr,a
775
776 ;; start tx
777 acall maybe_start_tx
778 ;acall dump_stat
779
780 pop acc
781 pop dph1
782 pop dpl1
783 pop dph
784 pop dpl
785 pop dps
786 reti
787
788dump_stat:
789 ;; fill in EP4in with a debugging message:
790 ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
791 ;; tx_active
792 ;; tx_ring[0..15]
793 ;; 0xfc
794 ;; rx_ring[0..15]
795 clr a
796 mov dps, a
797
798 mov dptr, IN4CS
799 movx a, @dptr
800 jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
801 mov dptr, IN4BUF
802
803 mov a, tx_ring_in
804 movx @dptr, a
805 inc dptr
806 mov a, tx_ring_out
807 movx @dptr, a
808 inc dptr
809
810 mov a, rx_ring_in
811 movx @dptr, a
812 inc dptr
813 mov a, rx_ring_out
814 movx @dptr, a
815 inc dptr
816
817 clr a
818 jnb TX_RUNNING, dump_stat__no_tx_running
819 inc a
820dump_stat__no_tx_running:
821 movx @dptr, a
822 inc dptr
823 ;; tx_ring[0..15]
824 inc dps
825 mov dptr, #tx_ring ; DPTR1: source
826 mov r1, #16
827dump_stat__tx_ring_loop:
828 movx a, @dptr
829 inc dptr
830 inc dps
831 movx @dptr, a
832 inc dptr
833 inc dps
834 djnz r1, dump_stat__tx_ring_loop
835 inc dps
836
837 mov a, #0xfc
838 movx @dptr, a
839 inc dptr
840
841 ;; rx_ring[0..15]
842 inc dps
843 mov dptr, #rx_ring ; DPTR1: source
844 mov r1, #16
845dump_stat__rx_ring_loop:
846 movx a, @dptr
847 inc dptr
848 inc dps
849 movx @dptr, a
850 inc dptr
851 inc dps
852 djnz r1, dump_stat__rx_ring_loop
853
854 ;; now send it
855 clr a
856 mov dps, a
857 mov dptr, IN4BC
858 mov a, #38
859 movx @dptr, a
860dump_stat__done:
861 ret
862
863;;; ============================================================
864
865maybe_start_tx:
866 ;; make sure the tx process is running.
867 jb TX_RUNNING, start_tx_done
868start_tx:
869 ;; is there work to be done?
870 mov a, tx_ring_in
871 cjne a,tx_ring_out, start_tx__work
872 ret ; no work
873start_tx__work:
874 ;; tx was not running. send the first character, setup the TI int
875 inc tx_ring_out ; [++tx_ring_out]
876 mov dph, #HIGH(tx_ring)
877 mov dpl, tx_ring_out
878 movx a, @dptr
879 mov sbuf, a
880 setb TX_RUNNING
881start_tx_done:
882 ;; can we unthrottle the host tx process?
883 ;; step 1: do we care?
884 mov a, #0
885 cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
886 ;; nope
887start_tx_really_done:
888 ret
889start_tx__maybe_unthrottle_tx:
890 ;; step 2: is there now room?
891 mov a, tx_ring_out
892 setb c
893 subb a, tx_ring_in
894 ;; a is now write_room. If thresh >= a, we can unthrottle
895 clr c
896 subb a, tx_unthrottle_threshold
897 jc start_tx_really_done ; nope
898 ;; yes, we can unthrottle. remove the threshold and mark a request
899 mov tx_unthrottle_threshold, #0
900 setb DO_TX_UNTHROTTLE
901 ;; prod rx, which will actually send the message when in2 becomes free
902 ljmp start_in
903
904
905serial_int:
906 push dps
907 push dpl
908 push dph
909 push dpl1
910 push dph1
911 push acc
912 jnb TI, serial_int__not_tx
913 ;; tx finished. send another character if we have one
914 clr TI ; clear int
915 clr TX_RUNNING
916 lcall start_tx
917serial_int__not_tx:
918 jnb RI, serial_int__not_rx
919 lcall get_rx_char
920 clr RI ; clear int
921serial_int__not_rx:
922 ;; return
923 pop acc
924 pop dph1
925 pop dpl1
926 pop dph
927 pop dpl
928 pop dps
929 reti
930
931get_rx_char:
932 mov dph, #HIGH(rx_ring)
933 mov dpl, rx_ring_in
934 inc dpl ; target = rx_ring_in+1
935 mov a, sbuf
936 movx @dptr, a
937 ;; check for overflow before incrementing rx_ring_in
938 mov a, dpl
939 cjne a, rx_ring_out, get_rx_char__no_overflow
940 ;; signal overflow
941 ret
942get_rx_char__no_overflow:
943 inc rx_ring_in
944 ;; kick off USB INpipe
945 acall start_in
946 ret
947
948start_in:
949 ;; check if the inpipe is already running.
950 mov dptr, IN2CS
951 movx a, @dptr
952 jb acc.1, start_in__done; int will handle it
953 jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
954 ;; see if there is any work to do. a serial interrupt might occur
955 ;; during this sequence?
956 mov a, rx_ring_in
957 cjne a, rx_ring_out, start_in__have_work
958 ret ; nope
959start_in__have_work:
960 ;; now copy as much data as possible into the pipe. 63 bytes max.
961 clr a
962 mov dps, a
963 mov dph, #HIGH(rx_ring) ; load DPTR0 with source
964 inc dps
965 mov dptr, IN2BUF ; load DPTR1 with target
966 movx @dptr, a ; in[0] signals that rest of IN is rx data
967 inc dptr
968 inc dps
969 ;; loop until we run out of data, or we have copied 64 bytes
970 mov r1, #1 ; INbuf size counter
971start_in__loop:
972 mov a, rx_ring_in
973 cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
974 sjmp start_in__kick
975start_inlocal_irq_enablell_copying:
976 inc rx_ring_out
977 mov dpl, rx_ring_out
978 movx a, @dptr
979 inc dps
980 movx @dptr, a ; write into IN buffer
981 inc dptr
982 inc dps
983 inc r1
984 cjne r1, #64, start_in__loop; loop
985start_in__kick:
986 ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
987 ;; kick off IN
988 mov dptr, IN2BC
989 mov a, r1
990 jz start_in__done
991 movx @dptr, a
992 ;; done
993start_in__done:
994 ;acall dump_stat
995 ret
996start_in__do_tx_unthrottle:
997 ;; special sequence: send a tx unthrottle message
998 clr DO_TX_UNTHROTTLE
999 clr a
1000 mov dps, a
1001 mov dptr, IN2BUF
1002 mov a, #1
1003 movx @dptr, a
1004 inc dptr
1005 mov a, #2
1006 movx @dptr, a
1007 mov dptr, IN2BC
1008 movx @dptr, a
1009 ret
1010
1011putchar:
1012 clr TI
1013 mov SBUF, a
1014putchar_wait:
1015 jnb TI, putchar_wait
1016 clr TI
1017 ret
1018
1019
1020baud_table: ; baud_high, then baud_low
1021 ;; baud[0]: 110
1022 .byte BAUD_HIGH(110)
1023 .byte BAUD_LOW(110)
1024 ;; baud[1]: 300
1025 .byte BAUD_HIGH(300)
1026 .byte BAUD_LOW(300)
1027 ;; baud[2]: 1200
1028 .byte BAUD_HIGH(1200)
1029 .byte BAUD_LOW(1200)
1030 ;; baud[3]: 2400
1031 .byte BAUD_HIGH(2400)
1032 .byte BAUD_LOW(2400)
1033 ;; baud[4]: 4800
1034 .byte BAUD_HIGH(4800)
1035 .byte BAUD_LOW(4800)
1036 ;; baud[5]: 9600
1037 .byte BAUD_HIGH(9600)
1038 .byte BAUD_LOW(9600)
1039 ;; baud[6]: 19200
1040 .byte BAUD_HIGH(19200)
1041 .byte BAUD_LOW(19200)
1042 ;; baud[7]: 38400
1043 .byte BAUD_HIGH(38400)
1044 .byte BAUD_LOW(38400)
1045 ;; baud[8]: 57600
1046 .byte BAUD_HIGH(57600)
1047 .byte BAUD_LOW(57600)
1048 ;; baud[9]: 115200
1049 .byte BAUD_HIGH(115200)
1050 .byte BAUD_LOW(115200)
1051
1052desc_device:
1053 .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
1054 .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
1055;;; The "real" device id, which must match the host driver, is that
1056;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1057
1058desc_config1:
1059 .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1060 .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1061 .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1062 .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1063
1064desc_strings:
1065 .word string_langids, string_mfg, string_product, string_serial
1066desc_strings_end:
1067
1068string_langids: .byte string_langids_end-string_langids
1069 .byte 3
1070 .word 0
1071string_langids_end:
1072
1073 ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
1074 ;; *that* is a pain in the ass to encode. And they are little-endian
1075 ;; too. Use this perl snippet to get the bytecodes:
1076 /* while (<>) {
1077 @c = split(//);
1078 foreach $c (@c) {
1079 printf("0x%02x, 0x00, ", ord($c));
1080 }
1081 }
1082 */
1083
1084string_mfg: .byte string_mfg_end-string_mfg
1085 .byte 3
1086; .byte "ACME usb widgets"
1087 .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
1088string_mfg_end:
1089
1090string_product: .byte string_product_end-string_product
1091 .byte 3
1092; .byte "ACME USB serial widget"
1093 .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
1094string_product_end:
1095
1096string_serial: .byte string_serial_end-string_serial
1097 .byte 3
1098; .byte "47"
1099 .byte 0x34, 0x00, 0x37, 0x00
1100string_serial_end:
1101
1102;;; ring buffer memory
1103 ;; tx_ring_in+1 is where the next input byte will go
1104 ;; [tx_ring_out] has been sent
1105 ;; if tx_ring_in == tx_ring_out, theres no work to do
1106 ;; there are (tx_ring_in - tx_ring_out) chars to be written
1107 ;; dont let _in lap _out
1108 ;; cannot inc if tx_ring_in+1 == tx_ring_out
1109 ;; write [tx_ring_in+1] then tx_ring_in++
1110 ;; if (tx_ring_in+1 == tx_ring_out), overflow
1111 ;; else tx_ring_in++
1112 ;; read/send [tx_ring_out+1], then tx_ring_out++
1113
1114 ;; rx_ring_in works the same way
1115
1116 .org 0x1000
1117tx_ring:
1118 .skip 0x100 ; 256 bytes
1119rx_ring:
1120 .skip 0x100 ; 256 bytes
1121
1122
1123 .END
1124
diff --git a/firmware/keyspan_pda/xircom_pgs.HEX b/firmware/keyspan_pda/xircom_pgs.HEX
new file mode 100644
index 000000000000..e9b00d70282f
--- /dev/null
+++ b/firmware/keyspan_pda/xircom_pgs.HEX
@@ -0,0 +1,87 @@
1:03000000020200F9
2:0400230002059B0037
3:050030000000000000CB
4:0400430002010000B6
5:100100000202BA000200000002000000020000002B
6:1001100002000000020000000200000002000000D7
7:100120000200000002000000020485000204B90081
8:1002000075815EE4F532F533F530F531F534C20031
9:10021000C201A90074FE901000F0A3D9FC74FD90F7
10:100220001100F0A3D9FC907F9474BFF0907F957477
11:10023000EFF07410907F9EF07400907F98F07440FF
12:10024000907F9DF07400907F97F07482907F9EF075
13:10025000907F957403F0907FAFE0D2E0F07401904E
14:100260007FABF0907FAEF0907FAC7404F0907FADE8
15:100270007404F0907FC9F07407907FAFF074209001
16:100280007F9CF07480907F98F07453F59875C83017
17:100290007B059144D2CA759850D2E8D2AFD2AC74E3
18:1002A00000F586907FD67402F0792E7A007B00DB11
19:1002B000FEDAFAD9F67406F080FEC086C082C083EA
20:1002C000C084C085C0E0E591C2E4F591907FAB7435
21:1002D00001F0907FE8E0F9A3E0FAA3E0FBA3E0FCE3
22:1002E000E95460B4000302035DB4406EBA000B121F
23:1002F0000444400302042602042EBA010302042E21
24:10030000BA020302042EBA0303020468BA041EBB35
25:10031000000A907F95E04402F0020426907F98E066
26:1003200054FDF0907F95E054FDF0020426BA0503D9
27:1003300002042EBA0619BB0008E533D39532020435
28:1003400002BB0108E532C3953302040202042EBA4F
29:1003500007058B3402042602042E02042EBA002064
30:10036000B98010907F00E4F0A3F0907FB57402F0A4
31:10037000020426B9820280EBB9810280E602042ED3
32:10038000BA010FBB000302042EBB010302042602C4
33:10039000042EBA030FBB000302042EBB01030204A8
34:1003A0002602042EBA0656BC010F907FD47406F0C4
35:1003B000907FD5745AF0020426BC0212BB006F90E5
36:1003C0007FD47406F0907FD5746CF0020426BC03D1
37:1003D000297404C39B40576055EB2B90068C2582F3
38:1003E000F58274003583F583E0F9A3E0FA907FD4B9
39:1003F000E9F0907FD5EAF002042602042EBA080F35
40:100400007401907F00F07401907FB5F0020426BA69
41:100410000903020426BA0A057400020402BA0B0397
42:1004200002042602042E907FB47402F08009907FAB
43:10043000B4E04401F08000D0E0D085D084D083D0F7
44:1004400082D08632EB20E71EC3940A5019EB232496
45:1004500046F58274063400F583E0F5CBF5CDA3E0D4
46:10046000F5CAF5CCC322D322B94111EB64FF548005
47:10047000FB907F98E0547F4BF0020426907F9BE036
48:1004800064FF020402C086C082C083C084C085C0ED
49:10049000E0E591C2E4F591907FA97404F074209096
50:1004A0007F9CF01205DC7420907F9CF0D0E0D0851A
51:1004B000D084D083D082D08632C086C082C083C030
52:1004C00084C085C0E07410907F9CF0E591C2E4F593
53:1004D00091907FAA7404F0907FC9E0F9E4F58690CA
54:1004E0007DC0758510853284E005860584F0E5843D
55:1004F000B53302800905320586A3D9EC8000907FD0
56:10050000C9F0B16D7420907F9CF0D0E0D085D0848C
57:10051000D083D082D08632E4F586907FBCE020E1A3
58:100520004B907D00E532F0A3E533F0A3E530F0A376
59:10053000E531F0A3E430000104F0A305869010003B
60:100540007910E0A30586F0A30586D9F6058674FC2C
61:10055000F0A305869011007910E0A30586F0A305AD
62:1005600086D9F6E4F586907FBD7426F0222000132C
63:10057000E532B53301220533758310853382E0F50A
64:1005800099D2007400B5340122E533D39532C39576
65:100590003440F5753400D2010205DCC086C082C04B
66:1005A00083C084C085C0E0309907C299C20012059B
67:1005B000703098051205C6C298D0E0D085D084D09E
68:1005C00083D082D086327583118530820582E59989
69:1005D000F0E582B53101220530B1DC227410907F44
70:1005E0009CF0907FB8E020E13E20013CE530B53141
71:1005F0000122E4F5867583110586907E00F0A3053F
72:10060000867901E530B5310280100531853182E00F
73:100610000586F0A3058609B940E97410907F9CF027
74:10062000907FB9E96001F022C201E4F586907E0076
75:100630007401F0A37402F0907FB9F022C299F59989
76:100640003099FDC29922E55EF63CFD8FFEC8FF643D
77:10065000FFB2FFD9FFEDFFF3FFFA12010001FFFF28
78:10066000FF40CD06040189AB01020301090220000D
79:1006700001010080320904000002FFFFFF000705AE
80:10068000820340000107050202400000069406981C
81:1006900006BA06E8040300002203410043004D00AF
82:1006A000450020007500730062002000770069009B
83:1006B000640067006500740073002E03410043006E
84:1006C0004D004500200055005300420020007300FB
85:1006D00065007200690061006C002000770069000D
86:0E06E0006400670065007400060334003700F4
87:00000001FF
diff --git a/firmware/keyspan_pda/xircom_pgs.S b/firmware/keyspan_pda/xircom_pgs.S
new file mode 100644
index 000000000000..05d99dd63776
--- /dev/null
+++ b/firmware/keyspan_pda/xircom_pgs.S
@@ -0,0 +1,1192 @@
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 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)
169
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
180
181;;; local variables. These are not initialized properly: do it by hand.
182 .org 30H
183rx_ring_in: .byte 0
184rx_ring_out: .byte 0
185tx_ring_in: .byte 0
186tx_ring_out: .byte 0
187tx_unthrottle_threshold: .byte 0
188
189 .org 0x100H ; wants to be on a page boundary
190USB_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
215
216
217 .org 0x200
218
219start: 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
234clear_tx_ring_loop:
235 movx @dptr, a
236 inc dptr
237 djnz r1, clear_tx_ring_loop
238
239 mov a, #0xfd
240 mov dptr, #rx_ring
241clear_rx_ring_loop:
242 movx @dptr, a
243 inc dptr
244 djnz r1, clear_rx_ring_loop
245
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
259
260 ;; clear PC4
261 mov a, #0x00
262 mov dptr,OUTC
263 movx @dptr,a
264
265 ;; set OEB.6
266 mov a, #0x40
267 mov dptr,OEB
268 movx @dptr,a
269
270 ;; clear PB6
271 mov a, #0x00
272 mov dptr,OUTB
273 movx @dptr,a
274
275 ;; set OEC.[17]
276 mov a, #0x82
277 mov dptr,OEC
278 movx @dptr,a
279
280
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
292
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
308
309;; mov a, #0x84 ; turn on RTS, DTR
310;; mov dptr,OUTC
311;; movx @dptr, a
312
313 mov a, #0x7 ; turn on DTR
314 mov dptr,USBBAV
315 movx @dptr, a
316
317 mov a, #0x20 ; turn on the RED led
318 mov dptr,OEA
319 movx @dptr, a
320
321 mov a, #0x80 ; turn on RTS
322 mov dptr,OUTC
323 movx @dptr, a
324
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
348send:
349 mov SBUF, a
350 inc a
351 anl a, #0x3F
352 orl a, #0x40
353; xrl a, #0x02
354wait1:
355 jnb TI, wait1
356 clr TI
357 djnz r1, send
358;done: sjmp done
359
360#endif
361
362 setb EUSB
363 setb EA
364 setb ES0
365 ;acall dump_stat
366
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
375renum_wait1:
376 mov r2, #0
377renum_wait2:
378 mov r3, #0
379renum_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
388main: sjmp main
389
390
391
392ISR_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
405
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
419
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
427setup_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
437setup_bmreq_type_not_standard__do_stall:
438 ljmp setup_stall
439setup_ctrl_not_00:
440 cjne r2, #0x01, setup_ctrl_not_01
441 ;; 01 is reserved for set bits (parity). TODO
442 ljmp setup_stall
443setup_ctrl_not_01:
444 cjne r2, #0x02, setup_ctrl_not_02
445 ;; 02 is set HW flow control. TODO
446 ljmp setup_stall
447setup_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
452setup_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
462setup_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
473setup_ctrl_not_04:
474 cjne r2, #0x05, setup_ctrl_not_05
475 ;; 05 is set desired interrupt bitmap. TODO
476 ljmp setup_stall
477setup_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
486setup_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
493setup_ctrl_06_not_01:
494 ljmp setup_stall
495setup_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
500setup_ctrl_not_07:
501 ljmp setup_stall
502
503setup_bmreq_type_not_vendor:
504 ljmp setup_stall
505
506
507setup_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
514setup_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
524setup_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
530setup_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
534setup_Get_Status_not_interface:
535 ljmp setup_stall
536
537setup_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
543setup_Clear_Feature_not_stall:
544 cjne r3, #0x01, setup_Clear_Feature_not_rwake
545 ;; Clear_Feature(remote wakeup). ignored.
546 ljmp setup_done_ack
547setup_Clear_Feature_not_rwake:
548 ljmp setup_stall
549
550setup_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
556setup_Set_Feature_not_stall:
557 cjne r3, #0x01, setup_Set_Feature_not_rwake
558 ;; Set_Feature(remote wakeup). ignored.
559 ljmp setup_done_ack
560setup_Set_Feature_not_rwake:
561 ljmp setup_stall
562
563setup_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
575setup_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
587setup_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
629setup_Get_Descriptor_not_string:
630 ljmp setup_stall
631
632setup_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
637setup_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
644setup_breq_not_08:
645 cjne r2, #0x09, setup_breq_not_09
646 ;; 09: Set_Configuration. ignored.
647 ljmp setup_done_ack
648setup_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
654setup_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
658setup_breq_not_0b:
659 ljmp setup_stall
660
661
662setup_done_ack:
663 ;; now clear HSNAK
664 mov dptr, EP0CS
665 mov a, #0x02
666 movx @dptr, a
667 sjmp setup_done
668setup_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
677setup_done:
678 pop acc
679 pop dph1
680 pop dpl1
681 pop dph
682 pop dpl
683 pop dps
684 reti
685
686;;; ==============================================================
687
688set_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
716set_baud__badbaud:
717 setb c ; c=1: failure
718 ret
719
720;;; ==================================================
721control_pins:
722 cjne r1, #0x41, control_pins_in
723control_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
735control_pins_in:
736 mov dptr, PINSC
737 movx a, @dptr
738 xrl a, #0xff
739 ljmp setup_return_one_byte
740
741;;; ========================================
742
743ISR_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
756
757 mov a, #0x20 ; Turn off the green LED
758 mov dptr,OEA
759 movx @dptr, a
760
761
762 ;; do stuff
763 lcall start_in
764
765 mov a, #0x20 ; Turn off the green LED
766 mov dptr,OEA
767 movx @dptr, a
768
769
770
771 pop acc
772 pop dph1
773 pop dpl1
774 pop dph
775 pop dpl
776 pop dps
777 reti
778
779ISR_Ep2out:
780 push dps
781 push dpl
782 push dph
783 push dpl1
784 push dph1
785 push acc
786
787 mov a, #0x10 ; Turn the green LED
788 mov dptr,OEA
789 movx @dptr, a
790
791
792
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
799
800 ;; do stuff
801
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
811OUT_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
819OUT_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
825OUT_overflow:
826 ;; signal overflow
827 ;; fall through
828OUT_done:
829 ;; ack
830 mov dptr,OUT2BC
831 movx @dptr,a
832
833 ;; start tx
834 acall maybe_start_tx
835 ;acall dump_stat
836
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
848
849dump_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
870
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
881dump_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
888dump_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
906dump_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
921dump_stat__done:
922 ret
923
924;;; ============================================================
925
926maybe_start_tx:
927 ;; make sure the tx process is running.
928 jb TX_RUNNING, start_tx_done
929start_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
934start_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
942start_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
948start_tx_really_done:
949 ret
950start_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
965
966serial_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
978serial_int__not_tx:
979 jnb RI, serial_int__not_rx
980 lcall get_rx_char
981 clr RI ; clear int
982serial_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
991
992get_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
1003get_rx_char__no_overflow:
1004 inc rx_ring_in
1005 ;; kick off USB INpipe
1006 acall start_in
1007 ret
1008
1009start_in:
1010 ;; check if the inpipe is already running.
1011 mov a,#0x10
1012 mov dptr, OEA
1013 movx @dptr,a
1014
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
1024start_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
1036start_in__loop:
1037 mov a, rx_ring_in
1038 cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
1039 sjmp start_in__kick
1040start_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
1050start_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
1061start_in__done:
1062 ;acall dump_stat
1063 ret
1064start_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
1079putchar:
1080 clr TI
1081 mov SBUF, a
1082putchar_wait:
1083 jnb TI, putchar_wait
1084 clr TI
1085 ret
1086
1087
1088baud_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)
1119
1120desc_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
1126desc_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
1131
1132desc_strings:
1133 .word string_langids, string_mfg, string_product, string_serial
1134desc_strings_end:
1135
1136string_langids: .byte string_langids_end-string_langids
1137 .byte 3
1138 .word 0
1139string_langids_end:
1140
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 */
1151
1152string_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
1156string_mfg_end:
1157
1158string_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
1162string_product_end:
1163
1164string_serial: .byte string_serial_end-string_serial
1165 .byte 3
1166; .byte "47"
1167 .byte 0x34, 0x00, 0x37, 0x00
1168string_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++
1181
1182 ;; rx_ring_in works the same way
1183
1184 .org 0x1000
1185tx_ring:
1186 .skip 0x100 ; 256 bytes
1187rx_ring:
1188 .skip 0x100 ; 256 bytes
1189
1190
1191 .END
1192