Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/firmware/keyspan_pda/xircom_pgs.S
10821 views
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 <[email protected]>
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.
107
*
108
* Questions, bugs, patches to Brian.
109
*
110
* -Brian Warner <[email protected]>
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
183
rx_ring_in: .byte 0
184
rx_ring_out: .byte 0
185
tx_ring_in: .byte 0
186
tx_ring_out: .byte 0
187
tx_unthrottle_threshold: .byte 0
188
189
.org 0x100H ; wants to be on a page boundary
190
USB_Jump_Table:
191
ljmp ISR_Sudav ; Setup Data Available
192
.byte 0
193
ljmp 0 ; Start of Frame
194
.byte 0
195
ljmp 0 ; Setup Data Loading
196
.byte 0
197
ljmp 0 ; Global Suspend
198
.byte 0
199
ljmp 0 ; USB Reset
200
.byte 0
201
ljmp 0 ; Reserved
202
.byte 0
203
ljmp 0 ; End Point 0 In
204
.byte 0
205
ljmp 0 ; End Point 0 Out
206
.byte 0
207
ljmp 0 ; End Point 1 In
208
.byte 0
209
ljmp 0 ; End Point 1 Out
210
.byte 0
211
ljmp ISR_Ep2in
212
.byte 0
213
ljmp ISR_Ep2out
214
.byte 0
215
216
217
.org 0x200
218
219
start: mov SP,STACK-1 ; set stack
220
;; clear local variables
221
clr a
222
mov tx_ring_in, a
223
mov tx_ring_out, a
224
mov rx_ring_in, a
225
mov rx_ring_out, a
226
mov tx_unthrottle_threshold, a
227
clr TX_RUNNING
228
clr DO_TX_UNTHROTTLE
229
230
;; clear fifo with "fe"
231
mov r1, 0
232
mov a, #0xfe
233
mov dptr, #tx_ring
234
clear_tx_ring_loop:
235
movx @dptr, a
236
inc dptr
237
djnz r1, clear_tx_ring_loop
238
239
mov a, #0xfd
240
mov dptr, #rx_ring
241
clear_rx_ring_loop:
242
movx @dptr, a
243
inc dptr
244
djnz r1, clear_rx_ring_loop
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
348
send:
349
mov SBUF, a
350
inc a
351
anl a, #0x3F
352
orl a, #0x40
353
; xrl a, #0x02
354
wait1:
355
jnb TI, wait1
356
clr TI
357
djnz r1, send
358
;done: sjmp done
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
375
renum_wait1:
376
mov r2, #0
377
renum_wait2:
378
mov r3, #0
379
renum_wait3:
380
djnz r3, renum_wait3
381
djnz r2, renum_wait2
382
djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
383
mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
384
movx @dptr, a
385
;; we are back online. the host device will now re-query us
386
387
388
main: sjmp main
389
390
391
392
ISR_Sudav:
393
push dps
394
push dpl
395
push dph
396
push dpl1
397
push dph1
398
push acc
399
mov a,EXIF
400
clr acc.4
401
mov EXIF,a ; clear INT2 first
402
mov dptr, USBIRQ ; clear USB int
403
mov a,#01h
404
movx @dptr,a
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
427
setup_bmreq_type_not_standard:
428
;; a still has bmreq&0x60
429
cjne a, #0x40, setup_bmreq_type_not_vendor
430
;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
431
;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
432
cjne r2, #0x00, setup_ctrl_not_00
433
;; 00 is set baud, wValue[0] has baud rate index
434
lcall set_baud ; index in r3, carry set if error
435
jc setup_bmreq_type_not_standard__do_stall
436
ljmp setup_done_ack
437
setup_bmreq_type_not_standard__do_stall:
438
ljmp setup_stall
439
setup_ctrl_not_00:
440
cjne r2, #0x01, setup_ctrl_not_01
441
;; 01 is reserved for set bits (parity). TODO
442
ljmp setup_stall
443
setup_ctrl_not_01:
444
cjne r2, #0x02, setup_ctrl_not_02
445
;; 02 is set HW flow control. TODO
446
ljmp setup_stall
447
setup_ctrl_not_02:
448
cjne r2, #0x03, setup_ctrl_not_03
449
;; 03 is control pins (RTS, DTR).
450
ljmp control_pins ; will jump to setup_done_ack,
451
; or setup_return_one_byte
452
setup_ctrl_not_03:
453
cjne r2, #0x04, setup_ctrl_not_04
454
;; 04 is send break (really "turn break on/off"). TODO
455
cjne r3, #0x00, setup_ctrl_do_break_on
456
;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
457
mov dptr, PORTCCFG
458
movx a, @dptr
459
orl a, #0x02
460
movx @dptr, a
461
ljmp setup_done_ack
462
setup_ctrl_do_break_on:
463
;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
464
mov dptr, OUTC
465
movx a, @dptr
466
anl a, #0xfd ; ~0x02
467
movx @dptr, a
468
mov dptr, PORTCCFG
469
movx a, @dptr
470
anl a, #0xfd ; ~0x02
471
movx @dptr, a
472
ljmp setup_done_ack
473
setup_ctrl_not_04:
474
cjne r2, #0x05, setup_ctrl_not_05
475
;; 05 is set desired interrupt bitmap. TODO
476
ljmp setup_stall
477
setup_ctrl_not_05:
478
cjne r2, #0x06, setup_ctrl_not_06
479
;; 06 is query room
480
cjne r3, #0x00, setup_ctrl_06_not_00
481
;; 06, wValue[0]=0 is query write_room
482
mov a, tx_ring_out
483
setb c
484
subb a, tx_ring_in ; out-1-in = 255 - (in-out)
485
ljmp setup_return_one_byte
486
setup_ctrl_06_not_00:
487
cjne r3, #0x01, setup_ctrl_06_not_01
488
;; 06, wValue[0]=1 is query chars_in_buffer
489
mov a, tx_ring_in
490
clr c
491
subb a, tx_ring_out ; in-out
492
ljmp setup_return_one_byte
493
setup_ctrl_06_not_01:
494
ljmp setup_stall
495
setup_ctrl_not_06:
496
cjne r2, #0x07, setup_ctrl_not_07
497
;; 07 is request tx unthrottle interrupt
498
mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
499
ljmp setup_done_ack
500
setup_ctrl_not_07:
501
ljmp setup_stall
502
503
setup_bmreq_type_not_vendor:
504
ljmp setup_stall
505
506
507
setup_bmreq_is_standard:
508
cjne r2, #0x00, setup_breq_not_00
509
;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
510
cjne r1, #0x80, setup_Get_Status_not_device
511
;; Get_Status(device)
512
;; are we self-powered? no. can we do remote wakeup? no
513
;; so return two zero bytes. This is reusable
514
setup_return_two_zero_bytes:
515
mov dptr, IN0BUF
516
clr a
517
movx @dptr, a
518
inc dptr
519
movx @dptr, a
520
mov dptr, IN0BC
521
mov a, #2
522
movx @dptr, a
523
ljmp setup_done_ack
524
setup_Get_Status_not_device:
525
cjne r1, #0x82, setup_Get_Status_not_endpoint
526
;; Get_Status(endpoint)
527
;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
528
;; for now: cheat. TODO
529
sjmp setup_return_two_zero_bytes
530
setup_Get_Status_not_endpoint:
531
cjne r1, #0x81, setup_Get_Status_not_interface
532
;; Get_Status(interface): return two zeros
533
sjmp setup_return_two_zero_bytes
534
setup_Get_Status_not_interface:
535
ljmp setup_stall
536
537
setup_breq_not_00:
538
cjne r2, #0x01, setup_breq_not_01
539
;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
540
cjne r3, #0x00, setup_Clear_Feature_not_stall
541
;; Clear_Feature(stall). should clear a stall bit. TODO
542
ljmp setup_stall
543
setup_Clear_Feature_not_stall:
544
cjne r3, #0x01, setup_Clear_Feature_not_rwake
545
;; Clear_Feature(remote wakeup). ignored.
546
ljmp setup_done_ack
547
setup_Clear_Feature_not_rwake:
548
ljmp setup_stall
549
550
setup_breq_not_01:
551
cjne r2, #0x03, setup_breq_not_03
552
;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
553
cjne r3, #0x00, setup_Set_Feature_not_stall
554
;; Set_Feature(stall). Should set a stall bit. TODO
555
ljmp setup_stall
556
setup_Set_Feature_not_stall:
557
cjne r3, #0x01, setup_Set_Feature_not_rwake
558
;; Set_Feature(remote wakeup). ignored.
559
ljmp setup_done_ack
560
setup_Set_Feature_not_rwake:
561
ljmp setup_stall
562
563
setup_breq_not_03:
564
cjne r2, #0x06, setup_breq_not_06
565
;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
566
cjne r4, #0x01, setup_Get_Descriptor_not_device
567
;; Get_Descriptor(device)
568
mov dptr, SUDPTRH
569
mov a, #HIGH(desc_device)
570
movx @dptr, a
571
mov dptr, SUDPTRL
572
mov a, #LOW(desc_device)
573
movx @dptr, a
574
ljmp setup_done_ack
575
setup_Get_Descriptor_not_device:
576
cjne r4, #0x02, setup_Get_Descriptor_not_config
577
;; Get_Descriptor(config[n])
578
cjne r3, #0x00, setup_stall; only handle n==0
579
;; Get_Descriptor(config[0])
580
mov dptr, SUDPTRH
581
mov a, #HIGH(desc_config1)
582
movx @dptr, a
583
mov dptr, SUDPTRL
584
mov a, #LOW(desc_config1)
585
movx @dptr, a
586
ljmp setup_done_ack
587
setup_Get_Descriptor_not_config:
588
cjne r4, #0x03, setup_Get_Descriptor_not_string
589
;; Get_Descriptor(string[wValueL])
590
;; if (wValueL >= maxstrings) stall
591
mov a, #((desc_strings_end-desc_strings)/2)
592
clr c
593
subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
594
jc setup_stall
595
jz setup_stall
596
mov a, r3
597
add a, r3 ; a = 2*wValueL
598
mov dptr, #desc_strings
599
add a, dpl
600
mov dpl, a
601
mov a, #0
602
addc a, dph
603
mov dph, a ; dph = desc_strings[a]. big endian! (handy)
604
;; it looks like my adapter uses a revision of the EZUSB that
605
;; contains "rev D errata number 8", as hinted in the EzUSB example
606
;; code. I cannot find an actual errata description on the Cypress
607
;; web site, but from the example code it looks like this bug causes
608
;; the length of string descriptors to be read incorrectly, possibly
609
;; sending back more characters than the descriptor has. The workaround
610
;; is to manually send out all of the data. The consequence of not
611
;; using the workaround is that the strings gathered by the kernel
612
;; driver are too long and are filled with trailing garbage (including
613
;; leftover strings). Writing this out by hand is a nuisance, so for
614
;; now I will just live with the bug.
615
movx a, @dptr
616
mov r1, a
617
inc dptr
618
movx a, @dptr
619
mov r2, a
620
mov dptr, SUDPTRH
621
mov a, r1
622
movx @dptr, a
623
mov dptr, SUDPTRL
624
mov a, r2
625
movx @dptr, a
626
;; done
627
ljmp setup_done_ack
628
629
setup_Get_Descriptor_not_string:
630
ljmp setup_stall
631
632
setup_breq_not_06:
633
cjne r2, #0x08, setup_breq_not_08
634
;; Get_Configuration. always 1. return one byte.
635
;; this is reusable
636
mov a, #1
637
setup_return_one_byte:
638
mov dptr, IN0BUF
639
movx @dptr, a
640
mov a, #1
641
mov dptr, IN0BC
642
movx @dptr, a
643
ljmp setup_done_ack
644
setup_breq_not_08:
645
cjne r2, #0x09, setup_breq_not_09
646
;; 09: Set_Configuration. ignored.
647
ljmp setup_done_ack
648
setup_breq_not_09:
649
cjne r2, #0x0a, setup_breq_not_0a
650
;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
651
;; since we only have one interface, ignore wIndexL, return a 0
652
mov a, #0
653
ljmp setup_return_one_byte
654
setup_breq_not_0a:
655
cjne r2, #0x0b, setup_breq_not_0b
656
;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
657
ljmp setup_done_ack
658
setup_breq_not_0b:
659
ljmp setup_stall
660
661
662
setup_done_ack:
663
;; now clear HSNAK
664
mov dptr, EP0CS
665
mov a, #0x02
666
movx @dptr, a
667
sjmp setup_done
668
setup_stall:
669
;; unhandled. STALL
670
;EP0CS |= bmEPSTALL
671
mov dptr, EP0CS
672
movx a, @dptr
673
orl a, EP0STALLbit
674
movx @dptr, a
675
sjmp setup_done
676
677
setup_done:
678
pop acc
679
pop dph1
680
pop dpl1
681
pop dph
682
pop dpl
683
pop dps
684
reti
685
686
;;; ==============================================================
687
688
set_baud: ; baud index in r3
689
;; verify a < 10
690
mov a, r3
691
jb ACC.7, set_baud__badbaud
692
clr c
693
subb a, #10
694
jnc set_baud__badbaud
695
mov a, r3
696
rl a ; a = index*2
697
add a, #LOW(baud_table)
698
mov dpl, a
699
mov a, #HIGH(baud_table)
700
addc a, #0
701
mov dph, a
702
;; TODO: shut down xmit/receive
703
;; TODO: wait for current xmit char to leave
704
;; TODO: shut down timer to avoid partial-char glitch
705
movx a,@dptr ; BAUD_HIGH
706
mov RCAP2H, a
707
mov TH2, a
708
inc dptr
709
movx a,@dptr ; BAUD_LOW
710
mov RCAP2L, a
711
mov TL2, a
712
;; TODO: restart xmit/receive
713
;; TODO: reenable interrupts, resume tx if pending
714
clr c ; c=0: success
715
ret
716
set_baud__badbaud:
717
setb c ; c=1: failure
718
ret
719
720
;;; ==================================================
721
control_pins:
722
cjne r1, #0x41, control_pins_in
723
control_pins_out:
724
;TODO BKPT is DTR
725
mov a, r3 ; wValue[0] holds new bits: b7 is new RTS
726
xrl a, #0xff ; 1 means active, 0V, +12V ?
727
anl a, #0x80
728
mov r3, a
729
mov dptr, OUTC
730
movx a, @dptr ; only change bit 7
731
anl a, #0x7F ; ~0x84
732
orl a, r3
733
movx @dptr, a ; other pins are inputs, bits ignored
734
ljmp setup_done_ack
735
control_pins_in:
736
mov dptr, PINSC
737
movx a, @dptr
738
xrl a, #0xff
739
ljmp setup_return_one_byte
740
741
;;; ========================================
742
743
ISR_Ep2in:
744
push dps
745
push dpl
746
push dph
747
push dpl1
748
push dph1
749
push acc
750
mov a,EXIF
751
clr acc.4
752
mov EXIF,a ; clear INT2 first
753
mov dptr, IN07IRQ ; clear USB int
754
mov a,#04h
755
movx @dptr,a
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
779
ISR_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
811
OUT_loop:
812
movx a,@dptr ; read
813
inc dps ; switch to DPTR1: target
814
inc dpl1 ; target = tx_ring_in+1
815
movx @dptr,a ; store
816
mov a,dpl1
817
cjne a, tx_ring_out, OUT_no_overflow
818
sjmp OUT_overflow
819
OUT_no_overflow:
820
inc tx_ring_in ; tx_ring_in++
821
inc dps ; switch to DPTR0: source
822
inc dptr
823
djnz r1, OUT_loop
824
sjmp OUT_done
825
OUT_overflow:
826
;; signal overflow
827
;; fall through
828
OUT_done:
829
;; ack
830
mov dptr,OUT2BC
831
movx @dptr,a
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
849
dump_stat:
850
;; fill in EP4in with a debugging message:
851
;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
852
;; tx_active
853
;; tx_ring[0..15]
854
;; 0xfc
855
;; rx_ring[0..15]
856
clr a
857
mov dps, a
858
859
mov dptr, IN4CS
860
movx a, @dptr
861
jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
862
mov dptr, IN4BUF
863
864
mov a, tx_ring_in
865
movx @dptr, a
866
inc dptr
867
mov a, tx_ring_out
868
movx @dptr, a
869
inc dptr
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
881
dump_stat__no_tx_running:
882
movx @dptr, a
883
inc dptr
884
;; tx_ring[0..15]
885
inc dps
886
mov dptr, #tx_ring ; DPTR1: source
887
mov r1, #16
888
dump_stat__tx_ring_loop:
889
movx a, @dptr
890
inc dptr
891
inc dps
892
movx @dptr, a
893
inc dptr
894
inc dps
895
djnz r1, dump_stat__tx_ring_loop
896
inc dps
897
898
mov a, #0xfc
899
movx @dptr, a
900
inc dptr
901
902
;; rx_ring[0..15]
903
inc dps
904
mov dptr, #rx_ring ; DPTR1: source
905
mov r1, #16
906
dump_stat__rx_ring_loop:
907
movx a, @dptr
908
inc dptr
909
inc dps
910
movx @dptr, a
911
inc dptr
912
inc dps
913
djnz r1, dump_stat__rx_ring_loop
914
915
;; now send it
916
clr a
917
mov dps, a
918
mov dptr, IN4BC
919
mov a, #38
920
movx @dptr, a
921
dump_stat__done:
922
ret
923
924
;;; ============================================================
925
926
maybe_start_tx:
927
;; make sure the tx process is running.
928
jb TX_RUNNING, start_tx_done
929
start_tx:
930
;; is there work to be done?
931
mov a, tx_ring_in
932
cjne a,tx_ring_out, start_tx__work
933
ret ; no work
934
start_tx__work:
935
;; tx was not running. send the first character, setup the TI int
936
inc tx_ring_out ; [++tx_ring_out]
937
mov dph, #HIGH(tx_ring)
938
mov dpl, tx_ring_out
939
movx a, @dptr
940
mov sbuf, a
941
setb TX_RUNNING
942
start_tx_done:
943
;; can we unthrottle the host tx process?
944
;; step 1: do we care?
945
mov a, #0
946
cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
947
;; nope
948
start_tx_really_done:
949
ret
950
start_tx__maybe_unthrottle_tx:
951
;; step 2: is there now room?
952
mov a, tx_ring_out
953
setb c
954
subb a, tx_ring_in
955
;; a is now write_room. If thresh >= a, we can unthrottle
956
clr c
957
subb a, tx_unthrottle_threshold
958
jc start_tx_really_done ; nope
959
;; yes, we can unthrottle. remove the threshold and mark a request
960
mov tx_unthrottle_threshold, #0
961
setb DO_TX_UNTHROTTLE
962
;; prod rx, which will actually send the message when in2 becomes free
963
ljmp start_in
964
965
966
serial_int:
967
push dps
968
push dpl
969
push dph
970
push dpl1
971
push dph1
972
push acc
973
jnb TI, serial_int__not_tx
974
;; tx finished. send another character if we have one
975
clr TI ; clear int
976
clr TX_RUNNING
977
lcall start_tx
978
serial_int__not_tx:
979
jnb RI, serial_int__not_rx
980
lcall get_rx_char
981
clr RI ; clear int
982
serial_int__not_rx:
983
;; return
984
pop acc
985
pop dph1
986
pop dpl1
987
pop dph
988
pop dpl
989
pop dps
990
reti
991
992
get_rx_char:
993
mov dph, #HIGH(rx_ring)
994
mov dpl, rx_ring_in
995
inc dpl ; target = rx_ring_in+1
996
mov a, sbuf
997
movx @dptr, a
998
;; check for overflow before incrementing rx_ring_in
999
mov a, dpl
1000
cjne a, rx_ring_out, get_rx_char__no_overflow
1001
;; signal overflow
1002
ret
1003
get_rx_char__no_overflow:
1004
inc rx_ring_in
1005
;; kick off USB INpipe
1006
acall start_in
1007
ret
1008
1009
start_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
1024
start_in__have_work:
1025
;; now copy as much data as possible into the pipe. 63 bytes max.
1026
clr a
1027
mov dps, a
1028
mov dph, #HIGH(rx_ring) ; load DPTR0 with source
1029
inc dps
1030
mov dptr, IN2BUF ; load DPTR1 with target
1031
movx @dptr, a ; in[0] signals that rest of IN is rx data
1032
inc dptr
1033
inc dps
1034
;; loop until we run out of data, or we have copied 64 bytes
1035
mov r1, #1 ; INbuf size counter
1036
start_in__loop:
1037
mov a, rx_ring_in
1038
cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
1039
sjmp start_in__kick
1040
start_inlocal_irq_enablell_copying:
1041
inc rx_ring_out
1042
mov dpl, rx_ring_out
1043
movx a, @dptr
1044
inc dps
1045
movx @dptr, a ; write into IN buffer
1046
inc dptr
1047
inc dps
1048
inc r1
1049
cjne r1, #64, start_in__loop; loop
1050
start_in__kick:
1051
;; either we ran out of data, or we copied 64 bytes. r1 has byte count
1052
;; kick off IN
1053
mov a, #0x10 ; Turn the green LED
1054
mov dptr,OEA
1055
movx @dptr, a
1056
mov dptr, IN2BC
1057
mov a, r1
1058
jz start_in__done
1059
movx @dptr, a
1060
;; done
1061
start_in__done:
1062
;acall dump_stat
1063
ret
1064
start_in__do_tx_unthrottle:
1065
;; special sequence: send a tx unthrottle message
1066
clr DO_TX_UNTHROTTLE
1067
clr a
1068
mov dps, a
1069
mov dptr, IN2BUF
1070
mov a, #1
1071
movx @dptr, a
1072
inc dptr
1073
mov a, #2
1074
movx @dptr, a
1075
mov dptr, IN2BC
1076
movx @dptr, a
1077
ret
1078
1079
putchar:
1080
clr TI
1081
mov SBUF, a
1082
putchar_wait:
1083
jnb TI, putchar_wait
1084
clr TI
1085
ret
1086
1087
1088
baud_table: ; baud_high, then baud_low
1089
;; baud[0]: 110
1090
.byte BAUD_HIGH(110)
1091
.byte BAUD_LOW(110)
1092
;; baud[1]: 300
1093
.byte BAUD_HIGH(300)
1094
.byte BAUD_LOW(300)
1095
;; baud[2]: 1200
1096
.byte BAUD_HIGH(1200)
1097
.byte BAUD_LOW(1200)
1098
;; baud[3]: 2400
1099
.byte BAUD_HIGH(2400)
1100
.byte BAUD_LOW(2400)
1101
;; baud[4]: 4800
1102
.byte BAUD_HIGH(4800)
1103
.byte BAUD_LOW(4800)
1104
;; baud[5]: 9600
1105
.byte BAUD_HIGH(9600)
1106
.byte BAUD_LOW(9600)
1107
;; baud[6]: 19200
1108
.byte BAUD_HIGH(19200)
1109
.byte BAUD_LOW(19200)
1110
;; baud[7]: 38400
1111
.byte BAUD_HIGH(38400)
1112
.byte BAUD_LOW(38400)
1113
;; baud[8]: 57600
1114
.byte BAUD_HIGH(57600)
1115
.byte BAUD_LOW(57600)
1116
;; baud[9]: 115200
1117
.byte BAUD_HIGH(115200)
1118
.byte BAUD_LOW(115200)
1119
1120
desc_device:
1121
.byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
1122
.byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
1123
;;; The "real" device id, which must match the host driver, is that
1124
;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1125
1126
desc_config1:
1127
.byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1128
.byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1129
.byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1130
.byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1131
1132
desc_strings:
1133
.word string_langids, string_mfg, string_product, string_serial
1134
desc_strings_end:
1135
1136
string_langids: .byte string_langids_end-string_langids
1137
.byte 3
1138
.word 0
1139
string_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
1152
string_mfg: .byte string_mfg_end-string_mfg
1153
.byte 3
1154
; .byte "ACME usb widgets"
1155
.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
1156
string_mfg_end:
1157
1158
string_product: .byte string_product_end-string_product
1159
.byte 3
1160
; .byte "ACME USB serial widget"
1161
.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
1162
string_product_end:
1163
1164
string_serial: .byte string_serial_end-string_serial
1165
.byte 3
1166
; .byte "47"
1167
.byte 0x34, 0x00, 0x37, 0x00
1168
string_serial_end:
1169
1170
;;; ring buffer memory
1171
;; tx_ring_in+1 is where the next input byte will go
1172
;; [tx_ring_out] has been sent
1173
;; if tx_ring_in == tx_ring_out, theres no work to do
1174
;; there are (tx_ring_in - tx_ring_out) chars to be written
1175
;; dont let _in lap _out
1176
;; cannot inc if tx_ring_in+1 == tx_ring_out
1177
;; write [tx_ring_in+1] then tx_ring_in++
1178
;; if (tx_ring_in+1 == tx_ring_out), overflow
1179
;; else tx_ring_in++
1180
;; read/send [tx_ring_out+1], then tx_ring_out++
1181
1182
;; rx_ring_in works the same way
1183
1184
.org 0x1000
1185
tx_ring:
1186
.skip 0x100 ; 256 bytes
1187
rx_ring:
1188
.skip 0x100 ; 256 bytes
1189
1190
1191
.END
1192
1193
1194